From 659594792ab74a9eaca7a5422f9f0abfa5645e7a Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 4 Dec 2017 23:08:28 +0800 Subject: [PATCH 001/643] add unit test --- test/Web/DemoControllerTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/Web/DemoControllerTest.php b/test/Web/DemoControllerTest.php index 340eb6d7..4279d426 100644 --- a/test/Web/DemoControllerTest.php +++ b/test/Web/DemoControllerTest.php @@ -35,4 +35,13 @@ public function actionLayout() $response->assertSuccessful()->assertSee('Swoft')->assertSee('使用布局文件'); } + /** + * @test + */ + public function actionI18n() + { + $response = $this->request('GET', '/demo2/i18n', [], parent::ACCEPT_VIEW); + $response->assertSuccessful()->assertSee('title'); + } + } \ No newline at end of file From 25d8a3cf338a6cfed4d7cfecab3e87de70d0a26b Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 12 Dec 2017 22:31:05 +0800 Subject: [PATCH 002/643] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8action=E5=85=B3=E9=94=AE=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/DemoController.php | 16 ++++++------ app/Controllers/ErrorController.php | 2 +- app/Controllers/IndexController.php | 10 ++++---- app/Controllers/OrmController.php | 40 ++++++++++++++--------------- app/Controllers/Psr7Controller.php | 16 ++++++------ app/Controllers/RedisController.php | 2 +- app/Controllers/RestController.php | 12 ++++----- app/Controllers/RouteController.php | 14 +++++----- test/Web/IndexControllerTest.php | 6 ++--- test/Web/RedisControllerTest.php | 2 +- test/Web/RestTest.php | 12 ++++----- test/Web/RouteTest.php | 14 +++++----- 12 files changed, 73 insertions(+), 73 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index 3678a148..eff3a220 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -63,7 +63,7 @@ class DemoController * * @return array */ - public function actionIndex(Request $request) + public function index(Request $request) { // 获取所有GET参数 $get = $request->query(); @@ -85,7 +85,7 @@ public function actionIndex(Request $request) * 定义一个route,支持get,以"/"开头的定义,直接是根路径,处理uri=/index2 * @RequestMapping(route="/index2", method=RequestMethod::GET) */ - public function actionIndex2() + public function index2() { Coroutine::create(function () { App::trace("this is child trace" . Coroutine::id()); @@ -100,7 +100,7 @@ public function actionIndex2() /** * 没有使用注解,自动解析注入,默认支持get和post */ - public function actionTask() + public function task() { $result = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_COR); $mysql = Task::deliver('test', 'testMysql', [], Task::TYPE_COR); @@ -111,7 +111,7 @@ public function actionTask() return [$rpc, $http, $mysql, $result, $result1]; } - public function actionIndex6() + public function index6() { throw new Exception('AAAA'); // $a = $b; @@ -123,7 +123,7 @@ public function actionIndex6() /** * 子协程测试 */ - public function actionCor() + public function cor() { // 创建子协程 Coroutine::create(function () { @@ -143,7 +143,7 @@ public function actionCor() /** * 国际化测试 */ - public function actionI18n() + public function i18n() { $data[] = App::t("title", [], 'zh'); $data[] = App::t("title", [], 'en'); @@ -158,7 +158,7 @@ public function actionI18n() * @RequestMapping() * @View(template="demo/view") */ - public function actionView() + public function view() { $data = [ 'name' => 'Swoft', @@ -176,7 +176,7 @@ public function actionView() * @RequestMapping() * @View(template="demo/content", layout="layouts/default.php") */ - public function actionLayout() + public function layout() { $layout = 'layouts/default.php'; $data = [ diff --git a/app/Controllers/ErrorController.php b/app/Controllers/ErrorController.php index 0bb5c8ae..469f3270 100644 --- a/app/Controllers/ErrorController.php +++ b/app/Controllers/ErrorController.php @@ -22,7 +22,7 @@ class ErrorController * 错误action * @RequestMapping() */ - public function actionIndex() + public function index() { $response = App::getResponse(); $exception = $response->getException(); diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 023785f9..38d7aa50 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -24,7 +24,7 @@ class IndexController * @View(template="index/index") * @return array */ - public function actionIndex() + public function index() { $name = 'Swoft'; $notes = [ @@ -62,7 +62,7 @@ public function actionIndex() * @View(template="index/index") * @return \Swoft\Contract\Arrayable|__anonymous@836 */ - public function actionArrayable() + public function arrayable() { return (new class implements Arrayable { @@ -106,7 +106,7 @@ public function toArray(): array * @RequestMapping() * @return string */ - public function actionRaw() + public function raw() { $name = 'Swoft'; return $name; @@ -115,7 +115,7 @@ public function actionRaw() /** * @RequestMapping() */ - public function actionException() + public function exception() { throw new BadRequestException(); } @@ -127,7 +127,7 @@ public function actionException() * * @return \Swoft\Base\Response */ - public function actionRedirect(Response $response) + public function redirect(Response $response) { return $response->redirect('/'); } diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 7f88cadc..1e571f6e 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -24,7 +24,7 @@ class OrmController /** * AR save操作 */ - public function actionArSave() + public function arSave() { // $user = new User(); // // $user->setId(120); @@ -51,7 +51,7 @@ public function actionArSave() /** * EM查找 */ - public function actionSave() + public function save() { $user = new User(); $user->setName("stelin"); @@ -71,7 +71,7 @@ public function actionSave() /** * 实体内容删除 */ - public function actionArDelete() + public function arDelete() { $user = new User(); // $user->setId(286); @@ -86,7 +86,7 @@ public function actionArDelete() /** * Em 删除 */ - public function actionDelete() + public function delete() { $user = new User(); $user->setId(418); @@ -102,7 +102,7 @@ public function actionDelete() /** * EM deleteId */ - public function actionDeleteId() + public function deleteId() { $em = EntityManager::create(); // $result = $em->deleteById(Count::class, 396); @@ -114,7 +114,7 @@ public function actionDeleteId() /** * EM DeleteIds */ - public function actionDeleteIds() + public function deleteIds() { $em = EntityManager::create(); // $result = $em->deleteByIds(Count::class, [409, 410]); @@ -126,7 +126,7 @@ public function actionDeleteIds() /** * 删除ID测试 */ - public function actionArDeleteId() + public function arDeleteId() { // $result = User::deleteById(284); $result = User::deleteById(287, true); @@ -137,7 +137,7 @@ public function actionArDeleteId() /** * 删除IDs测试 */ - public function actionArDeleteIds() + public function arDeleteIds() { // $result = User::deleteByIds([291, 292]); $result = User::deleteByIds([288, 289], true); @@ -148,7 +148,7 @@ public function actionArDeleteIds() /** * 更新操作 */ - public function actionArUpdate() + public function arUpdate() { $query = User::findById(285); @@ -167,7 +167,7 @@ public function actionArUpdate() /** * 实体查找 */ - public function actionArFind() + public function arFind() { $user = new User(); $user->setSex(1); @@ -190,7 +190,7 @@ public function actionArFind() /** * EM find */ - public function actionFind() + public function find() { $user = new User(); $user->setSex(1); @@ -209,7 +209,7 @@ public function actionFind() /** * Ar ID查找 */ - public function actionArFindId() + public function arFindId() { $query = User::findById(236); $result = $query->getResult(); @@ -229,7 +229,7 @@ public function actionArFindId() /** * EM find id */ - public function actionFindId() + public function findId() { $em = EntityManager::create(); $query = $em->findById(User::class, 396); @@ -245,7 +245,7 @@ public function actionFindId() /** * Ar IDS查找 */ - public function actionArFindIds() + public function arFindIds() { $query = User::findByIds([416, 417]); @@ -262,7 +262,7 @@ public function actionArFindIds() /** * EM find ids */ - public function actionFindIds() + public function findIds() { $em = EntityManager::create(); $query = $em->findByIds(User::class, [396, 403]); @@ -278,7 +278,7 @@ public function actionFindIds() /** * Ar Query */ - public function actionArQuery() + public function arQuery() { // $query = User::query()->select('*')->andWhere('sex', 1)->orderBy('id',QueryBuilder::ORDER_BY_DESC)->limit(3); // $query = User::query()->selects(['id', 'sex' => 'sex2'])->andWhere('sex', 1)->orderBy('id',QueryBuilder::ORDER_BY_DESC)->limit(3); @@ -293,7 +293,7 @@ public function actionArQuery() /** * EM 事务测试 */ - public function actionTs() + public function ts() { $user = new User(); $user->setName("stelin"); @@ -321,7 +321,7 @@ public function actionTs() return [$uid, $result]; } - public function actionQuery() + public function query() { $em = EntityManager::create(); $query = $em->createQuery(); @@ -338,7 +338,7 @@ public function actionQuery() /** * 并发执行两个语句 */ - public function actionArCon() + public function arCon() { $query1 = User::query()->selects(['id', 'sex' => 'sex2'])->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 419) ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->getDefer(); @@ -352,7 +352,7 @@ public function actionArCon() } - public function actionSql() + public function sql() { $params = [ ['uid', 433], diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index 4a477205..8076b632 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -24,7 +24,7 @@ class Psr7Controller * * @return array */ - public function actionGet(Request $request) + public function get(Request $request) { $param1 = $request->query('param1'); $param2 = $request->query('param2', 'defaultValue'); @@ -38,7 +38,7 @@ public function actionGet(Request $request) * * @return array */ - public function actionPost(Request $request) + public function post(Request $request) { $param1 = $request->post('param1'); $param2 = $request->post('param2'); @@ -52,7 +52,7 @@ public function actionPost(Request $request) * * @return array */ - public function actionInput(Request $request) + public function input(Request $request) { $param1 = $request->input('param1'); $inputs = $request->input(); @@ -69,7 +69,7 @@ public function actionInput(Request $request) * * @return array */ - public function actionRaw(Request $request) + public function raw(Request $request) { $param1 = $request->raw(); return compact('param1'); @@ -81,7 +81,7 @@ public function actionRaw(Request $request) * * @return array */ - public function actionCookies(Request $request) + public function cookies(Request $request) { $cookie1 = $request->cookie(); return compact('cookie1'); @@ -94,7 +94,7 @@ public function actionCookies(Request $request) * * @return array */ - public function actionHeader(Request $request) + public function header(Request $request) { $header1 = $request->header(); $host = $request->header('host'); @@ -107,7 +107,7 @@ public function actionHeader(Request $request) * * @return array */ - public function actionJson(Request $request) + public function json(Request $request) { $json = $request->json(); $jsonParam = $request->json('jsonParam'); @@ -120,7 +120,7 @@ public function actionJson(Request $request) * * @return array */ - public function actionFiles(Request $request) + public function files(Request $request) { $files = $request->file(); foreach ($files as $file) { diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 5378ab6b..c1f74f47 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -22,7 +22,7 @@ class RedisController * @RequestMapping() * @return bool|string */ - public function actionTest() + public function test() { $setResult = RedisClient::set('test', 123321); $getResult = RedisClient::get('test'); diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php index ef9775bf..75ef64d3 100644 --- a/app/Controllers/RestController.php +++ b/app/Controllers/RestController.php @@ -27,7 +27,7 @@ class RestController * * @RequestMapping(route="/user", method={RequestMethod::GET}) */ - public function actionList() + public function list() { return ['list']; } @@ -43,7 +43,7 @@ public function actionList() * * @return array */ - public function actionCreate(Request $request) + public function create(Request $request) { $name = $request->input('name'); @@ -63,7 +63,7 @@ public function actionCreate(Request $request) * * @return array */ - public function actionGetUser(int $uid) + public function getUser(int $uid) { return ['getUser', $uid]; } @@ -79,7 +79,7 @@ public function actionGetUser(int $uid) * * @return array */ - public function actionGetBookFromUser(int $userId, string $bookId) + public function getBookFromUser(int $userId, string $bookId) { return ['bookFromUser', $userId, $bookId]; } @@ -94,7 +94,7 @@ public function actionGetBookFromUser(int $userId, string $bookId) * * @return array */ - public function actionDeleteUser(int $uid) + public function deleteUser(int $uid) { return ['delete', $uid]; } @@ -109,7 +109,7 @@ public function actionDeleteUser(int $uid) * @param Request $request * @return array */ - public function actionUpdateUser(Request $request, int $uid) + public function updateUser(Request $request, int $uid) { $body = $request->getBodyParams(); $body['update'] = 'update'; diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 05a63632..6c7eba1b 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -40,7 +40,7 @@ public function index() * * @return array */ - public function actionFuncArgs(bool $bool, Request $request, int $bid, string $name, int $uid, Response $response) + public function funcArgs(bool $bool, Request $request, int $bid, string $name, int $uid, Response $response) { return [$bid, $uid, $bool, $name, get_class($request), get_class($response)]; } @@ -50,7 +50,7 @@ public function actionFuncArgs(bool $bool, Request $request, int $bid, string $n * * @return string */ - public function actionHasNotArgs() + public function hasNotArgs() { return 'hasNotArg'; } @@ -62,7 +62,7 @@ public function actionHasNotArgs() * * @return string */ - public function actionHasAnyArgs(Request $request, int $bid) + public function hasAnyArgs(Request $request, int $bid) { return [get_class($request), $bid]; } @@ -75,7 +75,7 @@ public function actionHasAnyArgs(Request $request, int $bid) * * @return array */ - public function actionHasMoreArgs(Request $request, int $bid) + public function hasMoreArgs(Request $request, int $bid) { return [get_class($request), $bid]; } @@ -88,7 +88,7 @@ public function actionHasMoreArgs(Request $request, int $bid) * @param string $name * @return array */ - public function actionOptionalParameter(string $name) + public function optionalParameter(string $name) { return[$name]; } @@ -111,7 +111,7 @@ public function funcAnyName(string $name) * * @return array */ - public function actionNotAnnotation(Request $request) + public function notAnnotation(Request $request) { return [get_class($request)]; } @@ -131,7 +131,7 @@ public function onlyFunc(Request $request) * * @return array */ - public function BehindAction(Request $request) + public function behind(Request $request) { return [get_class($request)]; } diff --git a/test/Web/IndexControllerTest.php b/test/Web/IndexControllerTest.php index 1fa0da68..1387e487 100644 --- a/test/Web/IndexControllerTest.php +++ b/test/Web/IndexControllerTest.php @@ -19,7 +19,7 @@ class IndexControllerTest extends AbstractTestCase * @test * @covers \App\Controllers\IndexController */ - public function actionIndex() + public function testIndex() { $expectedResult = [ 'name' => 'Swoft', @@ -89,7 +89,7 @@ public function actionIndex() * @test * @covers \App\Controllers\IndexController */ - public function actionException() + public function testException() { $response = $this->request('GET', '/index/exception', [], parent::ACCEPT_JSON); $response->assertStatus(400)->assertJson(['message' => 'Bad Request']); @@ -99,7 +99,7 @@ public function actionException() * @test * @covers \App\Controllers\IndexController */ - public function actionRaw() + public function testRaw() { $expected = 'Swoft'; $response = $this->request('GET', '/index/raw', [], parent::ACCEPT_RAW); diff --git a/test/Web/RedisControllerTest.php b/test/Web/RedisControllerTest.php index e750e563..2eda997a 100644 --- a/test/Web/RedisControllerTest.php +++ b/test/Web/RedisControllerTest.php @@ -20,7 +20,7 @@ class RedisControllerTest extends AbstractTestCase * @requires extension redis * @covers \App\Controllers\RedisController */ - public function actionTest() + public function testTest() { $expected = [ 'setResult' => true, diff --git a/test/Web/RestTest.php b/test/Web/RestTest.php index 30765ddd..f48535e3 100644 --- a/test/Web/RestTest.php +++ b/test/Web/RestTest.php @@ -14,7 +14,7 @@ class RestTest extends AbstractTestCase { /** - * @covers \App\Controllers\RestController@actionList + * @covers \App\Controllers\RestController@list */ public function testList() { @@ -24,7 +24,7 @@ public function testList() } /** - * @covers \App\Controllers\RestController@actionCreate + * @covers \App\Controllers\RestController@create */ public function testCreate() { @@ -46,7 +46,7 @@ public function testCreate() } /** - * @covers \App\Controllers\RestController@actionGetUser + * @covers \App\Controllers\RestController@getUser */ public function testGetUser() { @@ -56,7 +56,7 @@ public function testGetUser() } /** - * @covers \App\Controllers\RestController@actionGetBookFromUser + * @covers \App\Controllers\RestController@getBookFromUser */ public function testGetBookFromUser() { @@ -66,7 +66,7 @@ public function testGetBookFromUser() } /** - * @covers \App\Controllers\RestController@actionDeleteUser + * @covers \App\Controllers\RestController@deleteUser */ public function testDeleteUser() { @@ -76,7 +76,7 @@ public function testDeleteUser() } /** - * @covers \App\Controllers\RestController@actionUpdateUser + * @covers \App\Controllers\RestController@updateUser */ public function testUpdateUser() { diff --git a/test/Web/RouteTest.php b/test/Web/RouteTest.php index 5beec3c6..ccf6c565 100644 --- a/test/Web/RouteTest.php +++ b/test/Web/RouteTest.php @@ -14,7 +14,7 @@ class RouteTest extends AbstractTestCase { /** - * @covers \App\Controllers\RouteController@actionFuncArgs + * @covers \App\Controllers\RouteController@funcArgs */ public function testFuncArgs() { @@ -49,7 +49,7 @@ public function testClosureFuncArgs() } /** - * @covers \App\Controllers\RouteController::actionHasNotArgs + * @covers \App\Controllers\RouteController::hasNotArgs */ public function testHasNotArg() { @@ -58,7 +58,7 @@ public function testHasNotArg() } /** - * @covers \App\Controllers\RouteController@actionHasAnyArgs + * @covers \App\Controllers\RouteController@hasAnyArgs */ public function testHasAnyArgs() { @@ -67,7 +67,7 @@ public function testHasAnyArgs() } /** - * @covers \App\Controllers\RouteController@actionOptionalParameter + * @covers \App\Controllers\RouteController@optionalParameter */ public function testOptionnalParameter() { @@ -79,7 +79,7 @@ public function testOptionnalParameter() } /** - * @covers \App\Controllers\RouteController@actionHasMoreArgs + * @covers \App\Controllers\RouteController@hasMoreArgs */ public function testHasMoreArgs() { @@ -88,7 +88,7 @@ public function testHasMoreArgs() } /** - * @covers \App\Controllers\RouteController@actionNotAnnotation + * @covers \App\Controllers\RouteController@notAnnotation */ public function testNotAnnotation() { @@ -106,7 +106,7 @@ public function testOnlyFunc() } /** - * @covers \App\Controllers\RouteController@BehindAction + * @covers \App\Controllers\RouteController@behind */ public function testBehindAction() { From e17b5ac29c2c1e25f5bc371fb9695a9587e76d01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Thu, 14 Dec 2017 20:13:55 +0800 Subject: [PATCH 003/643] Update Dockerfile Upgrade swoole to v2.0.10-stable --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7b6252b0..f2f8f996 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && ldconfig \ ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.0.10-rc3.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.0.10-stable.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From 845093f5be2ad307f183e38bb721c5070fc18348 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Fri, 15 Dec 2017 18:23:40 +0800 Subject: [PATCH 004/643] Modify $response->view() method, add $template and $layout properties for default path settings --- app/Controllers/IndexController.php | 39 ++++++++++++++++++++++++++--- test/Web/IndexControllerTest.php | 8 ++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 023785f9..f837f73d 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -20,7 +20,6 @@ class IndexController /** * @RequestMapping() - * * @View(template="index/index") * @return array */ @@ -102,6 +101,42 @@ public function toArray(): array }); } + /** + * @RequestMapping() + * @param \Swoft\Web\Response $response + * @return Response + */ + public function absolutePath(Response $response) + { + $template = '@res/views/index/index.php'; + return $response->view([ + 'name' => 'Swoft', + 'notes' => ['New Generation of PHP Framework', 'Hign Performance, Coroutine and Full Stack'], + 'links' => [ + [ + 'name' => 'Home', + 'link' => 'http://www.swoft.org/', + ], + [ + 'name' => 'Documentation', + 'link' => 'http://doc.swoft.org/', + ], + [ + 'name' => 'Case', + 'link' => 'http://swoft.org/case', + ], + [ + 'name' => 'Issue', + 'link' => 'https://github.com/swoft-cloud/swoft/issues', + ], + [ + 'name' => 'GitHub', + 'link' => 'https://github.com/swoft-cloud/swoft', + ], + ] + ], $template); + } + /** * @RequestMapping() * @return string @@ -122,9 +157,7 @@ public function actionException() /** * @RequestMapping() - * * @param \Swoft\Web\Response $response - * * @return \Swoft\Base\Response */ public function actionRedirect(Response $response) diff --git a/test/Web/IndexControllerTest.php b/test/Web/IndexControllerTest.php index 1fa0da68..a9cf0b41 100644 --- a/test/Web/IndexControllerTest.php +++ b/test/Web/IndexControllerTest.php @@ -83,6 +83,14 @@ public function actionIndex() ->assertSee($expectedResult['notes'][0]) ->assertSee($expectedResult['notes'][1]) ->assertHeader('Content-Type', 'text/html'); + + // absolutePath + $response = $this->request('GET', '/index/absolutePath', [], parent::ACCEPT_VIEW); + $response->assertSuccessful() + ->assertSee($expectedResult['name']) + ->assertSee($expectedResult['notes'][0]) + ->assertSee($expectedResult['notes'][1]) + ->assertHeader('Content-Type', 'text/html'); } /** From c0c3931d0bcc0534edc125d17f746ef84f810b8b Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 15 Dec 2017 22:08:29 +0800 Subject: [PATCH 005/643] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Breaker/UserBreaker.php | 48 ++++++++++++++++++++++++++++++++++++ app/Pool/UserServicePool.php | 21 ++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 app/Breaker/UserBreaker.php create mode 100644 app/Pool/UserServicePool.php diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php new file mode 100644 index 00000000..e040854a --- /dev/null +++ b/app/Breaker/UserBreaker.php @@ -0,0 +1,48 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class UserBreaker extends CircuitBreaker +{ + /** + * 连续失败次数,如果到达,状态切换为open + * + * @var int + */ + protected $swithToFailCount = 6; + + /** + * 连续成功次数,如果到达,状态切换为close + * @Value("${a.b.c}") + * @Value(env="a.b.c") + * @var int + */ + protected $swithToSuccessCount = 6; + + /** + * 单位毫秒 + * + * @var int + */ + protected $delaySwithTimer = 5000; + + + public function fallback($fallback = null) + { + + } +} \ No newline at end of file diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php new file mode 100644 index 00000000..b616679d --- /dev/null +++ b/app/Pool/UserServicePool.php @@ -0,0 +1,21 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class UserServicePool extends ServicePool +{ + +} \ No newline at end of file From 49f1879473f313558147513c5741f984ee2e34a7 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 15 Dec 2017 23:37:20 +0800 Subject: [PATCH 006/643] =?UTF-8?q?=E5=AE=9A=E4=B9=89=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Breaker/UserBreaker.php | 19 ++++++---------- app/Pool/UserServicePool.php | 42 ++++++++++++++++++++++++++++++++++- config/beans/service.php | 2 +- config/properties/app.php | 5 ++++- config/properties/cache.php | 15 +++++++++++++ config/properties/db.php | 4 ++++ config/properties/service.php | 4 ++++ 7 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 config/properties/cache.php create mode 100644 config/properties/db.php create mode 100644 config/properties/service.php diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php index e040854a..58f55adb 100644 --- a/app/Breaker/UserBreaker.php +++ b/app/Breaker/UserBreaker.php @@ -3,7 +3,6 @@ namespace App\Breaker; use Swoft\Bean\Annotation\Breaker; -use Swoft\Bean\Annotation\Value; use Swoft\Circuit\CircuitBreaker; /** @@ -19,30 +18,26 @@ class UserBreaker extends CircuitBreaker { /** - * 连续失败次数,如果到达,状态切换为open + * The number of successive failures + * If the arrival, the state switch to open * * @var int */ protected $swithToFailCount = 6; /** - * 连续成功次数,如果到达,状态切换为close - * @Value("${a.b.c}") - * @Value(env="a.b.c") + * The number of successive successes + * If the arrival, the state switch to close + * * @var int */ protected $swithToSuccessCount = 6; /** - * 单位毫秒 + * Switch close to open delay time + * The unit is milliseconds * * @var int */ protected $delaySwithTimer = 5000; - - - public function fallback($fallback = null) - { - - } } \ No newline at end of file diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index b616679d..2d7d705f 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -17,5 +17,45 @@ */ class UserServicePool extends ServicePool { - + /** + * the maximum number of idle connections + * + * @var int + */ + protected $maxIdel = 6; + + /** + * the maximum number of active connections + * + * @var int + */ + protected $maxActive = 50; + + /** + * the maximum number of wait connections + * + * @var int + */ + protected $maxWait = 100; + + /** + * the time of connect timeout + * + * @var int + */ + protected $timeout = 200; + + /** + * the addresses of connection + * + *
+     * [
+     *  '127.0.0.1:88',
+     *  '127.0.0.1:88'
+     * ]
+     * 
+ * + * @var array + */ + protected $uri = []; } \ No newline at end of file diff --git a/config/beans/service.php b/config/beans/service.php index 07210fa0..5d9b68de 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -11,7 +11,7 @@ 'type' => 'json', ], 'consulProvider' => [ - 'class' => \Swoft\Service\ConsulProvider::class, + 'class' => \Swoft\Service\ConsulProviderInterface::class, 'address' => '127.0.0.1:80', ], "userPool" => [ diff --git a/config/properties/app.php b/config/properties/app.php index 839d345e..94e78b40 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -19,5 +19,8 @@ 'user' => [ 'timeout' => 3000 ] - ] + ], + 'database' => require dirname(__FILE__).DS."db.php", + 'cache' => require dirname(__FILE__).DS."cache.php", + 'service' => require dirname(__FILE__).DS."service.php", ]; \ No newline at end of file diff --git a/config/properties/cache.php b/config/properties/cache.php new file mode 100644 index 00000000..8a1aab42 --- /dev/null +++ b/config/properties/cache.php @@ -0,0 +1,15 @@ + [ + "uri" => [ + '127.0.0.1:6379', + '127.0.0.1:6379', + ], + "maxIdel" => 6, + "maxActive" => 10, + "timeout" => 200, + "balancer" => 'random', + "useProvider" => false, + 'provider' => 'consul', + ], +]; \ No newline at end of file diff --git a/config/properties/db.php b/config/properties/db.php new file mode 100644 index 00000000..05e0b10e --- /dev/null +++ b/config/properties/db.php @@ -0,0 +1,4 @@ + Date: Sat, 16 Dec 2017 21:06:22 +0800 Subject: [PATCH 007/643] =?UTF-8?q?properties=E9=85=8D=E7=BD=AE=E5=92=8Cen?= =?UTF-8?q?v=E9=85=8D=E7=BD=AE=E5=B7=B2=E7=BB=8FRedis=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=B1=A0=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Pool/UserPoolConfig.php | 89 ++++++++++++++++++++++++++++++++++++ app/Pool/UserServicePool.php | 43 ++--------------- config/beans/base.php | 4 ++ config/properties/cache.php | 8 ++-- 4 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 app/Pool/UserPoolConfig.php diff --git a/app/Pool/UserPoolConfig.php b/app/Pool/UserPoolConfig.php new file mode 100644 index 00000000..582681dd --- /dev/null +++ b/app/Pool/UserPoolConfig.php @@ -0,0 +1,89 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class UserPoolConfig +{ + /** + * the maximum number of idle connections + * + * @Value(name="${{prefix.name}.maxIdel}", env="${{prefix.env}_MAX_IDEL}") + * @var int + */ + protected $maxIdel = 6; + + /** + * the maximum number of active connections + * + * @Value(name="${{prefix.name}.maxActive}", env="${{prefix.env}_MAX_ACTIVE}") + * @var int + */ + protected $maxActive = 50; + + /** + * the maximum number of wait connections + * + * @Value(name="${{prefix.name}.maxWait}", env="${{prefix.env}_MAX_WAIT}") + * @var int + */ + protected $maxWait = 100; + + /** + * the time of connect timeout + * + * @Value(name="${{prefix.name}.timeout}", env="${{prefix.env}_TIMEOUT}") + * @var int + */ + protected $timeout = 200; + + /** + * the addresses of connection + * + *
+     * [
+     *  '127.0.0.1:88',
+     *  '127.0.0.1:88'
+     * ]
+     * 
+ * + * @Value(name="${{prefix.name}.uri}", env="${{prefix.env}_URI}") + * @var array + */ + protected $uri = []; + + /** + * whether to user provider(consul/etcd/zookeeper) + * + * @Value(name="${{prefix.name}.useProvider}", env="${{prefix.env}_USE_PROVIDER}") + * @var bool + */ + protected $useProvider = false; + + /** + * the default balancer is random balancer + * + * @Value(name="${{prefix.name}.balancer}", env="${{prefix.env}_BALANCER}") + * @var string + */ + protected $balancer = BalancerManager::TYPE_RANDOM; + + /** + * the default provider is consul provider + * + * @Value(name="${{prefix.name}.provider}", env="${{prefix.env}_PROVIDER}") + * @var string + */ + protected $serviceProvider = null; + + + +} \ No newline at end of file diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index 2d7d705f..3065b4b3 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -2,6 +2,7 @@ namespace App\Pool; +use Swoft\Bean\Annotation\Inject; use Swoft\Bean\Annotation\Pool; use Swoft\Pool\ServicePool; @@ -9,6 +10,7 @@ * the pool of user service * * @Pool(name="user") + * * @uses UserServicePool * @version 2017年12月14日 * @author stelin @@ -18,44 +20,9 @@ class UserServicePool extends ServicePool { /** - * the maximum number of idle connections - * - * @var int - */ - protected $maxIdel = 6; - - /** - * the maximum number of active connections - * - * @var int - */ - protected $maxActive = 50; - - /** - * the maximum number of wait connections - * - * @var int - */ - protected $maxWait = 100; - - /** - * the time of connect timeout - * - * @var int - */ - protected $timeout = 200; - - /** - * the addresses of connection - * - *
-     * [
-     *  '127.0.0.1:88',
-     *  '127.0.0.1:88'
-     * ]
-     * 
+ * @Inject() * - * @var array + * @var \App\Pool\UserPoolConfig */ - protected $uri = []; + private $poolConfig; } \ No newline at end of file diff --git a/config/beans/base.php b/config/beans/base.php index ecb11a7d..f0eaec8b 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -10,6 +10,10 @@ 'errorAction' => '/error/index', 'useProvider' => false, ], + 'balancerSelector' => [ + 'balancers' =>[ + ] + ], 'httpRouter' => [ 'class' => \Swoft\Router\Http\HandlerMapping::class, 'ignoreLastSep' => false, diff --git a/config/properties/cache.php b/config/properties/cache.php index 8a1aab42..97f63a4f 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -1,13 +1,15 @@ [ + 'name' => 'redis', "uri" => [ '127.0.0.1:6379', '127.0.0.1:6379', ], - "maxIdel" => 6, - "maxActive" => 10, - "timeout" => 200, + "maxIdel" => 8, + "maxActive" => 8, + "maxWait" => 8, + "timeout" => 8, "balancer" => 'random', "useProvider" => false, 'provider' => 'consul', From 607633f4e0fc859da28420d3c90f9088b98d2f43 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 17 Dec 2017 22:47:38 +0800 Subject: [PATCH 008/643] =?UTF-8?q?RPC=E6=9C=8D=E5=8A=A1=E7=86=94=E6=96=AD?= =?UTF-8?q?=E5=99=A8=E5=92=8C=E8=BF=9E=E6=8E=A5=E6=B1=A0=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Breaker/UserBreaker.php | 4 +++ app/Pool/{ => Config}/UserPoolConfig.php | 39 +++++++++++++++--------- app/Pool/UserServicePool.php | 3 +- config/properties/db.php | 28 +++++++++++++++++ 4 files changed, 58 insertions(+), 16 deletions(-) rename app/Pool/{ => Config}/UserPoolConfig.php (52%) diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php index 58f55adb..a372b5d8 100644 --- a/app/Breaker/UserBreaker.php +++ b/app/Breaker/UserBreaker.php @@ -3,6 +3,7 @@ namespace App\Breaker; use Swoft\Bean\Annotation\Breaker; +use Swoft\Bean\Annotation\Value; use Swoft\Circuit\CircuitBreaker; /** @@ -21,6 +22,7 @@ class UserBreaker extends CircuitBreaker * The number of successive failures * If the arrival, the state switch to open * + * @Value(name="${config.breaker.user.failCount}", env="${USER_BREAKER_FAIL_COUNT}") * @var int */ protected $swithToFailCount = 6; @@ -29,6 +31,7 @@ class UserBreaker extends CircuitBreaker * The number of successive successes * If the arrival, the state switch to close * + * @Value(name="${config.breaker.user.SuccessCount}", env="${USER_BREAKER_SUCCESS_COUNT}") * @var int */ protected $swithToSuccessCount = 6; @@ -37,6 +40,7 @@ class UserBreaker extends CircuitBreaker * Switch close to open delay time * The unit is milliseconds * + * @Value(name="${config.breaker.user.delayTime}", env="${USER_BREAKER_DELAY_TIME}") * @var int */ protected $delaySwithTimer = 5000; diff --git a/app/Pool/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php similarity index 52% rename from app/Pool/UserPoolConfig.php rename to app/Pool/Config/UserPoolConfig.php index 582681dd..e8ff02cd 100644 --- a/app/Pool/UserPoolConfig.php +++ b/app/Pool/Config/UserPoolConfig.php @@ -1,10 +1,14 @@ @@ -13,10 +17,18 @@ */ class UserPoolConfig { + /** + * the name of pool + * + * @Value(name="${config.service.user.name}", env="${SERVICE_USER_NAME}") + * @var string + */ + protected $name = ""; + /** * the maximum number of idle connections * - * @Value(name="${{prefix.name}.maxIdel}", env="${{prefix.env}_MAX_IDEL}") + * @Value(name="${config.service.user.maxIdel}", env="${SERVICE_USER_MAX_IDEL}") * @var int */ protected $maxIdel = 6; @@ -24,7 +36,7 @@ class UserPoolConfig /** * the maximum number of active connections * - * @Value(name="${{prefix.name}.maxActive}", env="${{prefix.env}_MAX_ACTIVE}") + * @Value(name="${config.service.user.maxActive}", env="${SERVICE_USER_MAX_ACTIVE}") * @var int */ protected $maxActive = 50; @@ -32,7 +44,7 @@ class UserPoolConfig /** * the maximum number of wait connections * - * @Value(name="${{prefix.name}.maxWait}", env="${{prefix.env}_MAX_WAIT}") + * @Value(name="${config.service.user.maxWait}", env="${SERVICE_USER_MAX_WAIT}") * @var int */ protected $maxWait = 100; @@ -40,7 +52,7 @@ class UserPoolConfig /** * the time of connect timeout * - * @Value(name="${{prefix.name}.timeout}", env="${{prefix.env}_TIMEOUT}") + * @Value(name="${config.service.user.timeout}", env="${SERVICE_USER_TIMEOUT}") * @var int */ protected $timeout = 200; @@ -55,7 +67,7 @@ class UserPoolConfig * ] * * - * @Value(name="${{prefix.name}.uri}", env="${{prefix.env}_URI}") + * @Value(name="${config.service.user.uri}", env="${SERVICE_USER_URI}") * @var array */ protected $uri = []; @@ -63,7 +75,7 @@ class UserPoolConfig /** * whether to user provider(consul/etcd/zookeeper) * - * @Value(name="${{prefix.name}.useProvider}", env="${{prefix.env}_USE_PROVIDER}") + * @Value(name="${config.service.user.useProvider}", env="${SERVICE_USER_USE_PROVIDER}") * @var bool */ protected $useProvider = false; @@ -71,19 +83,16 @@ class UserPoolConfig /** * the default balancer is random balancer * - * @Value(name="${{prefix.name}.balancer}", env="${{prefix.env}_BALANCER}") + * @Value(name="${config.service.user.balancer}", env="${SERVICE_USER_BALANCER}") * @var string */ - protected $balancer = BalancerManager::TYPE_RANDOM; + protected $balancer = BalancerSelector::TYPE_RANDOM; /** * the default provider is consul provider * - * @Value(name="${{prefix.name}.provider}", env="${{prefix.env}_PROVIDER}") + * @Value(name="${config.service.user.provider}", env="${SERVICE_USER_PROVIDER}") * @var string */ - protected $serviceProvider = null; - - - + protected $provider = ProviderSelector::TYPE_CONSUL; } \ No newline at end of file diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index 3065b4b3..0d10456a 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -5,6 +5,7 @@ use Swoft\Bean\Annotation\Inject; use Swoft\Bean\Annotation\Pool; use Swoft\Pool\ServicePool; +use App\Pool\Config\UserPoolConfig; /** * the pool of user service @@ -22,7 +23,7 @@ class UserServicePool extends ServicePool /** * @Inject() * - * @var \App\Pool\UserPoolConfig + * @var UserPoolConfig */ private $poolConfig; } \ No newline at end of file diff --git a/config/properties/db.php b/config/properties/db.php index 05e0b10e..94dfbb13 100644 --- a/config/properties/db.php +++ b/config/properties/db.php @@ -1,4 +1,32 @@ [ + 'name' => 'redis', + "uri" => [ + '127.0.0.1:6379', + '127.0.0.1:6379', + ], + "maxIdel" => 8, + "maxActive" => 8, + "maxWait" => 8, + "timeout" => 8, + "balancer" => 'random', + "useProvider" => false, + 'provider' => 'consul', + ], + 'slave' => [ + 'name' => 'redis', + "uri" => [ + '127.0.0.1:6379', + '127.0.0.1:6379', + ], + "maxIdel" => 8, + "maxActive" => 8, + "maxWait" => 8, + "timeout" => 8, + "balancer" => 'random', + "useProvider" => false, + 'provider' => 'consul', + ], ]; \ No newline at end of file From 930006dd134f8b242d19670809636f47c6a19677 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Mon, 18 Dec 2017 23:39:52 +0800 Subject: [PATCH 009/643] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E6=B1=A0=E5=92=8CRPC=E8=BF=9E=E6=8E=A5=E6=B1=A0=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Breaker/UserBreaker.php | 6 +++--- app/Pool/Config/UserPoolConfig.php | 23 ++++++++++++---------- app/Pool/UserServicePool.php | 2 +- config/beans/base.php | 31 ++++++++++++++++++++---------- config/beans/db.php | 31 ------------------------------ config/beans/redis.php | 17 ---------------- config/beans/service.php | 29 ++++------------------------ config/properties/app.php | 3 +++ config/properties/breaker.php | 8 ++++++++ config/properties/db.php | 4 ++-- 10 files changed, 55 insertions(+), 99 deletions(-) delete mode 100644 config/beans/db.php delete mode 100644 config/beans/redis.php create mode 100644 config/properties/breaker.php diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php index a372b5d8..46f5935d 100644 --- a/app/Breaker/UserBreaker.php +++ b/app/Breaker/UserBreaker.php @@ -25,16 +25,16 @@ class UserBreaker extends CircuitBreaker * @Value(name="${config.breaker.user.failCount}", env="${USER_BREAKER_FAIL_COUNT}") * @var int */ - protected $swithToFailCount = 6; + protected $swithToFailCount = 3; /** * The number of successive successes * If the arrival, the state switch to close * - * @Value(name="${config.breaker.user.SuccessCount}", env="${USER_BREAKER_SUCCESS_COUNT}") + * @Value(name="${config.breaker.user.successCount}", env="${USER_BREAKER_SUCCESS_COUNT}") * @var int */ - protected $swithToSuccessCount = 6; + protected $swithToSuccessCount = 3; /** * Switch close to open delay time diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php index e8ff02cd..8622f106 100644 --- a/app/Pool/Config/UserPoolConfig.php +++ b/app/Pool/Config/UserPoolConfig.php @@ -4,6 +4,9 @@ use Swoft\Bean\Annotation\Bean; use Swoft\Bean\Annotation\Value; +use Swoft\Pool\BalancerSelector; +use Swoft\Pool\ProviderSelector; +use Swoft\Testing\Pool\Config\PropertyPoolConfig; /** * the config of service user @@ -15,12 +18,12 @@ * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ -class UserPoolConfig +class UserPoolConfig extends PropertyPoolConfig { /** * the name of pool * - * @Value(name="${config.service.user.name}", env="${SERVICE_USER_NAME}") + * @Value(name="${config.service.user.name}", env="${USER_POOL_NAME}") * @var string */ protected $name = ""; @@ -28,7 +31,7 @@ class UserPoolConfig /** * the maximum number of idle connections * - * @Value(name="${config.service.user.maxIdel}", env="${SERVICE_USER_MAX_IDEL}") + * @Value(name="${config.service.user.maxIdel}", env="${USER_POOL_MAX_IDEL}") * @var int */ protected $maxIdel = 6; @@ -36,7 +39,7 @@ class UserPoolConfig /** * the maximum number of active connections * - * @Value(name="${config.service.user.maxActive}", env="${SERVICE_USER_MAX_ACTIVE}") + * @Value(name="${config.service.user.maxActive}", env="${USER_POOL_MAX_ACTIVE}") * @var int */ protected $maxActive = 50; @@ -44,7 +47,7 @@ class UserPoolConfig /** * the maximum number of wait connections * - * @Value(name="${config.service.user.maxWait}", env="${SERVICE_USER_MAX_WAIT}") + * @Value(name="${config.service.user.maxWait}", env="${USER_POOL_MAX_WAIT}") * @var int */ protected $maxWait = 100; @@ -52,7 +55,7 @@ class UserPoolConfig /** * the time of connect timeout * - * @Value(name="${config.service.user.timeout}", env="${SERVICE_USER_TIMEOUT}") + * @Value(name="${config.service.user.timeout}", env="${USER_POOL_TIMEOUT}") * @var int */ protected $timeout = 200; @@ -67,7 +70,7 @@ class UserPoolConfig * ] * * - * @Value(name="${config.service.user.uri}", env="${SERVICE_USER_URI}") + * @Value(name="${config.service.user.uri}", env="${USER_POOL_URI}") * @var array */ protected $uri = []; @@ -75,7 +78,7 @@ class UserPoolConfig /** * whether to user provider(consul/etcd/zookeeper) * - * @Value(name="${config.service.user.useProvider}", env="${SERVICE_USER_USE_PROVIDER}") + * @Value(name="${config.service.user.useProvider}", env="${USER_POOL_USE_PROVIDER}") * @var bool */ protected $useProvider = false; @@ -83,7 +86,7 @@ class UserPoolConfig /** * the default balancer is random balancer * - * @Value(name="${config.service.user.balancer}", env="${SERVICE_USER_BALANCER}") + * @Value(name="${config.service.user.balancer}", env="${USER_POOL_BALANCER}") * @var string */ protected $balancer = BalancerSelector::TYPE_RANDOM; @@ -91,7 +94,7 @@ class UserPoolConfig /** * the default provider is consul provider * - * @Value(name="${config.service.user.provider}", env="${SERVICE_USER_PROVIDER}") + * @Value(name="${config.service.user.provider}", env="${USER_POOL_PROVIDER}") * @var string */ protected $provider = ProviderSelector::TYPE_CONSUL; diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index 0d10456a..f350ac64 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -25,5 +25,5 @@ class UserServicePool extends ServicePool * * @var UserPoolConfig */ - private $poolConfig; + protected $poolConfig; } \ No newline at end of file diff --git a/config/beans/base.php b/config/beans/base.php index f0eaec8b..024632f0 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -2,32 +2,43 @@ return [ 'dispatcherServer' => [ - 'class' => \Swoft\Web\DispatcherServer::class + 'class' => \Swoft\Web\DispatcherServer::class, ], - 'application' => [ + 'application' => [ 'id' => APP_NAME, 'name' => APP_NAME, 'errorAction' => '/error/index', 'useProvider' => false, ], 'balancerSelector' => [ - 'balancers' =>[ - ] + 'class' => \Swoft\Pool\BalancerSelector::class, + 'balancers' => [ + + ], + ], + 'providerSelector' => [ + 'class' => \Swoft\Pool\ProviderSelector::class, + 'providers' => [ + + ], ], - 'httpRouter' => [ + 'httpRouter' => [ 'class' => \Swoft\Router\Http\HandlerMapping::class, 'ignoreLastSep' => false, 'tmpCacheNumber' => 1000, 'matchAll' => '', ], - 'requestParser' =>[ - 'class' => \Swoft\Web\RequestParser::class + 'requestParser' => [ + 'class' => \Swoft\Web\RequestParser::class, + 'parsers' => [ + + ], ], - 'renderer' => [ + 'renderer' => [ 'class' => \Swoft\Web\ViewRenderer::class, 'viewsPath' => '@resources/views/', ], - 'eventManager' => [ - 'class' => \Swoft\Event\EventManager::class, + 'eventManager' => [ + 'class' => \Swoft\Event\EventManager::class, ], ]; diff --git a/config/beans/db.php b/config/beans/db.php deleted file mode 100644 index a605eca9..00000000 --- a/config/beans/db.php +++ /dev/null @@ -1,31 +0,0 @@ - [ - "class" => \Swoft\Pool\DbPool::class, - "uri" => [ - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8', - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8' - ], - "maxIdel" => 6, - "maxActive" => 10, - "timeout" => 200, - "balancer" => '${randomBalancer}', - "serviceName" => 'user', - "useProvider" => false, - 'driver' => \Swoft\Pool\DbPool::MYSQL - ], - "dbSlave" => [ - "class" => \Swoft\Pool\DbPool::class, - "uri" => [ - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8', - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8' - ], - "maxIdel" => 6, - "maxActive" => 10, - "timeout" => 200, - "balancer" => '${randomBalancer}', - "serviceName" => 'user', - "useProvider" => false, - 'driver' => \Swoft\Pool\DbPool::MYSQL - ], -]; \ No newline at end of file diff --git a/config/beans/redis.php b/config/beans/redis.php deleted file mode 100644 index e1e5e4b1..00000000 --- a/config/beans/redis.php +++ /dev/null @@ -1,17 +0,0 @@ - [ - 'class' => \Swoft\Pool\RedisPool::class, - "uri" => [ - '127.0.0.1:6379', - '127.0.0.1:6379' - ], - "maxIdel" => 6, - "maxActive" => 10, - "timeout" => '${config.Service.user.timeout}', - "balancer" => '${randomBalancer}', - "serviceName" => 'redis', - "useProvider" => false, - 'serviceprovider' => '${consulProvider}' - ], -]; \ No newline at end of file diff --git a/config/beans/service.php b/config/beans/service.php index 5d9b68de..54981a01 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -7,31 +7,10 @@ 'class' => \Swoft\Router\Service\HandlerMapping::class, ], 'servicePacker' => [ - 'class' => \Swoft\Service\ServicePacker::class, - 'type' => 'json', - ], - 'consulProvider' => [ - 'class' => \Swoft\Service\ConsulProviderInterface::class, - 'address' => '127.0.0.1:80', - ], - "userPool" => [ - "class" => \Swoft\Pool\ServicePool::class, - "uri" => [ - '127.0.0.1:8099', - '127.0.0.1:8099', + 'class' => \Swoft\Service\ServicePacker::class, + 'type' => 'json', + 'packers' => [ + ], - "maxIdel" => 6, - "maxActive" => 10, - "timeout" => '${config.Service.user.timeout}', - "balancer" => '${randomBalancer}', - "serviceName" => 'user', - "useProvider" => false, - 'serviceprovider' => '${consulProvider}', - ], - "userBreaker" => [ - 'class' => \Swoft\Circuit\CircuitBreaker::class, - 'swithToSuccessCount' => 6, // 请求成功次数上限(状态切换) - 'swithToFailCount' => 6, // 请求失败次数上限(状态切换) - 'delaySwithTimer' => 5000, // 开启状态切换到半开状态的延迟时间,单位毫秒 ], ]; \ No newline at end of file diff --git a/config/properties/app.php b/config/properties/app.php index 94e78b40..d7d73701 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -9,6 +9,8 @@ 'App\Tasks', 'App\Services', 'App\Process', + 'App\Breaker', + 'App\Pool', ], 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', @@ -23,4 +25,5 @@ 'database' => require dirname(__FILE__).DS."db.php", 'cache' => require dirname(__FILE__).DS."cache.php", 'service' => require dirname(__FILE__).DS."service.php", + 'breaker' => require dirname(__FILE__).DS."breaker.php", ]; \ No newline at end of file diff --git a/config/properties/breaker.php b/config/properties/breaker.php new file mode 100644 index 00000000..1081a258 --- /dev/null +++ b/config/properties/breaker.php @@ -0,0 +1,8 @@ + [ + 'failCount' => 3, + 'successCount' => 3, + 'delayTime' => 500, + ], +]; \ No newline at end of file diff --git a/config/properties/db.php b/config/properties/db.php index 94dfbb13..871df9cc 100644 --- a/config/properties/db.php +++ b/config/properties/db.php @@ -1,7 +1,7 @@ [ - 'name' => 'redis', + 'name' => 'master', "uri" => [ '127.0.0.1:6379', '127.0.0.1:6379', @@ -16,7 +16,7 @@ ], 'slave' => [ - 'name' => 'redis', + 'name' => 'slave', "uri" => [ '127.0.0.1:6379', '127.0.0.1:6379', From 0bbaa4a7ac6cffa1ec83c638205a5abb85785684 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 19 Dec 2017 17:24:55 +0800 Subject: [PATCH 010/643] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E5=9B=BE=EF=BC=8C=E5=90=8E=E9=9D=A2=E9=87=8D=E6=96=B0=E7=BB=98?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index eab5241d..07d12ec7 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,6 @@ - Inotify 自动 Reload - 强大的日志系统 -# 系统架构 - -

-     - swoft - -

- # 文档 [**中文文档**](https://doc.swoft.org) From 2b8b1d3e65409ce2d09552d04ab45969e5af771d Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 19 Dec 2017 22:47:53 +0800 Subject: [PATCH 011/643] =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=B1=A0=E6=94=B9?= =?UTF-8?q?=E6=88=90=E4=BA=86=E9=85=8D=E7=BD=AE=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 49 +++++++++++++++++++++++++++++++ app/Controllers/OrmController.php | 6 ++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 0a7b3039..4027f193 100644 --- a/.env.example +++ b/.env.example @@ -30,3 +30,52 @@ DAEMONIZE=0 DISPATCH_MODE=2 LOG_FILE=@runtime/logs/swoole.log TASK_WORKER_NUM=1 + +# the pool of master nodes pool +DB_NAME=dbMaster +DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8 +DB_MAX_IDEL=6 +DB_MAX_ACTIVE=10 +DB_MAX_WAIT=20 +DB_TIMEOUT=200 +DB_USE_PROVIDER=false +DB_BALANCER=random +DB_PROVIDER=consul + +# the pool of slave nodes pool +DB_SLAVE_NAME=dbSlave +DB_SLAVE_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8 +DB_SLAVE_MAX_IDEL=6 +DB_SLAVE_MAX_ACTIVE=10 +DB_SLAVE_MAX_WAIT=20 +DB_SLAVE_TIMEOUT=200 +DB_SLAVE_USE_PROVIDER=false +DB_SLAVE_BALANCER=random +DB_SLAVE_PROVIDER=consul + +# the pool of redis +REDIS_NAME=redis +REDIS_URI=127.0.0.1:6379,127.0.0.1:6379 +REDIS_MAX_IDEL=6 +REDIS_MAX_ACTIVE=10 +REDIS_MAX_WAIT=20 +REDIS_TIMEOUT=200 +REDIS_USE_PROVIDER=false +REDIS_BALANCER=random +REDIS_PROVIDER=consul + +# the pool of user service +USER_POOL_NAME=user +USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099 +USER_POOL_MAX_IDEL=6 +USER_POOL_MAX_ACTIVE=10 +USER_POOL_MAX_WAIT=20 +USER_POOL_TIMEOUT=200 +USER_POOL_USE_PROVIDER=false +USER_POOL_BALANCER=random +USER_POOL_PROVIDER=consul + +# the breaker of user service +USER_BREAKER_FAIL_COUNT = 3 +USER_BREAKER_SUCCESS_COUNT = 6 +USER_BREAKER_DELAY_TIME = 5000 diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 1e571f6e..1a2d85d3 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -45,7 +45,7 @@ public function arSave() $count->setFans(mt_rand(1, 1000)); $count->setFollows(mt_rand(1, 1000)); - return $count->save(); + return [$count->save()]; } /** @@ -211,13 +211,13 @@ public function find() */ public function arFindId() { - $query = User::findById(236); + $query = User::findById(425); $result = $query->getResult(); /* @var User $userObject */ $userObject = $query->getResult(User::class); - $query = User::findById(238); + $query = User::findById(426); // $deferResult = $query->getDefer()->getResult(); /* @var User $deferResult */ From 0eab33d9483e68ae2afe583b67064b073cfde995 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 19 Dec 2017 23:57:40 +0800 Subject: [PATCH 012/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=EF=BC=8C=E9=94=99=E8=AF=AF=E5=8D=95=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Breaker/UserBreaker.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php index 46f5935d..06b165ac 100644 --- a/app/Breaker/UserBreaker.php +++ b/app/Breaker/UserBreaker.php @@ -25,7 +25,7 @@ class UserBreaker extends CircuitBreaker * @Value(name="${config.breaker.user.failCount}", env="${USER_BREAKER_FAIL_COUNT}") * @var int */ - protected $swithToFailCount = 3; + protected $switchToFailCount = 3; /** * The number of successive successes @@ -34,7 +34,7 @@ class UserBreaker extends CircuitBreaker * @Value(name="${config.breaker.user.successCount}", env="${USER_BREAKER_SUCCESS_COUNT}") * @var int */ - protected $swithToSuccessCount = 3; + protected $switchToSuccessCount = 3; /** * Switch close to open delay time @@ -43,5 +43,5 @@ class UserBreaker extends CircuitBreaker * @Value(name="${config.breaker.user.delayTime}", env="${USER_BREAKER_DELAY_TIME}") * @var int */ - protected $delaySwithTimer = 5000; + protected $delaySwitchTimer = 500; } \ No newline at end of file From 677ab7e8e8fd6238bb42f8f470d274bdfb503654 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 21 Dec 2017 18:28:00 +0800 Subject: [PATCH 013/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07d12ec7..3712c43b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/badge/license-apache%202.0-lightgrey.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) # 简介 -基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,框架全协程实现,性能大大优于传统的 PHP-FPM 模式。 +基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解等等。 - 基于 Swoole 扩展 - 内置 HTTP 协程服务器 From fe9d61033a5198f28c04e6f8bc376a4dbc6c4fe2 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 21 Dec 2017 18:37:41 +0800 Subject: [PATCH 014/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3712c43b..a308e367 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/badge/license-apache%202.0-lightgrey.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) # 简介 -基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解等等。 +基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解、强大的全局容器等等。 - 基于 Swoole 扩展 - 内置 HTTP 协程服务器 From c8870706c4f9f804171a46f6285079b462b281a1 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 21 Dec 2017 18:38:41 +0800 Subject: [PATCH 015/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a308e367..01bf7db2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/badge/license-apache%202.0-lightgrey.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) # 简介 -基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解、强大的全局容器等等。 +基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解、强大的全局容器、完善的服务治理等等。 - 基于 Swoole 扩展 - 内置 HTTP 协程服务器 From 01fce7fb34ccef2b0e7eb4b428b3597c3f95876b Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Thu, 21 Dec 2017 22:46:17 +0800 Subject: [PATCH 016/643] add team memeber daydaygo --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eab5241d..0171111d 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ php bin/swoft rpc:stop - [ccinn](https://github.com/whiteCcinn) (471113744@qq.com) - [esion](https://github.com/esion1) (esionwong@126.com) - [huangzhhui](https://github.com/huangzhhui) (huangzhwork@gmail.com) +- [daydaygo](https://github.com/daydaygo) (1252409767@qq.com) # 协议 Swoft的开源协议为Apache-2.0,详情参见[LICENSE](LICENSE)。 From 0ca461f31bb743aea9c2c85ac3dc41612323ceba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 22 Dec 2017 18:30:38 +0800 Subject: [PATCH 017/643] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8ec4cce5..8ec28f2f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ php: install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - - pecl install -f swoole-2.0.9 + - pecl install -f swoole-2.0.10 before_script: - composer update --dev From eae6fc9c2c90991d307bf317c264f03ec11aac8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Thu, 28 Dec 2017 15:03:42 +0800 Subject: [PATCH 018/643] Update Dockerfile Add zlib --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index f2f8f996..4a570baa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ RUN apt-get update \ git \ vim \ zip \ + libz-dev \ && apt-get clean RUN curl -sS https://getcomposer.org/installer | php \ From 1760c5d08ab2c7016714da911d5c7931c6aa2012 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 28 Dec 2017 22:37:20 +0800 Subject: [PATCH 019/643] =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b050d243..c272d0d4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/badge/license-apache%202.0-lightgrey.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) # 简介 -基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解、强大的全局容器、完善的服务治理等等。 +首个基于 Swoole 原生协程,新时代PHP高性能协程框架,内置 HTTP 服务器,常驻内存,不依赖传统的 PHP-FPM,没有复杂的异步回调、没有繁琐的yield, 有类似 Go 语言的协程、灵活的注解、强大的全局容器、完善的服务治理等等。 - 基于 Swoole 扩展 - 内置 HTTP 协程服务器 From 426f3ebc85168ba3d678682b51128010e679c26c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 29 Dec 2017 00:06:11 +0800 Subject: [PATCH 020/643] Update Dockerfile Upgrade swoole to v2.0.11 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4a570baa..c501433e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && ldconfig \ ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.0.10-stable.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.0.11.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From d90c5fd36d5d3a768f95c81d60bd59d2f3800c0f Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 29 Dec 2017 23:43:18 +0800 Subject: [PATCH 021/643] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/changelog.md b/changelog.md index 6b59805c..2f7d88d3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,16 @@ + +# 2017-12-26 + +重构连接池 + +# 2017-12-20 + +新增匹配事件 + +# 2017-12-16 + +重构热更新兼容跨平台 + # 2017-12-11 * 事件管理改动,使用 psr 14 实现 From 7fd085b1a724f8de3019591de421ceaac252c618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sat, 30 Dec 2017 18:42:22 +0800 Subject: [PATCH 022/643] Update Dockerfile Upgrade swoole to v2.0.12 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c501433e..8664a04d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && ldconfig \ ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.0.11.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.0.12.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From e7b18c78424e1512af131da2adbb89f7ce9f9d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sun, 31 Dec 2017 17:29:22 +0800 Subject: [PATCH 023/643] Update changelog.md --- changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 2f7d88d3..c8022845 100644 --- a/changelog.md +++ b/changelog.md @@ -1,15 +1,15 @@ # 2017-12-26 -重构连接池 +* 重构连接池 # 2017-12-20 -新增匹配事件 +* 新增匹配事件 # 2017-12-16 -重构热更新兼容跨平台 +* 重构热更新兼容跨平台 # 2017-12-11 From a524faf7d54a790f8d9e1e825784a0a105afd721 Mon Sep 17 00:00:00 2001 From: Zhaohui Huang Date: Mon, 1 Jan 2018 02:21:30 +0800 Subject: [PATCH 024/643] Update .travis.yml Upgrade swoole to v2.0.12 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8ec28f2f..9023df1e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ php: install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - - pecl install -f swoole-2.0.10 + - pecl install -f swoole-2.0.12 before_script: - composer update --dev From ba5d1173cd17eaf64b578f5b0a75295a204e2d77 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 2 Jan 2018 22:39:13 +0800 Subject: [PATCH 025/643] =?UTF-8?q?=E6=96=B0=E5=A2=9ERedis=E5=8D=95?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/RedisController.php | 95 +++++++++++++++++++++++++---- test/Web/RedisControllerTest.php | 84 ++++++++++++++++++++++--- 2 files changed, 159 insertions(+), 20 deletions(-) diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index c1f74f47..e7e89a2d 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -4,8 +4,9 @@ use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; -use Swoft\Cache\Redis\RedisClient; +use Swoft\Bean\Annotation\Inject; +use Swoft\Cache\CacheInterface; +use Swoft\Cache\Redis\CacheRedis; /** @@ -19,18 +20,90 @@ class RedisController { /** - * @RequestMapping() - * @return bool|string + * @Inject() + * @var CacheInterface */ - public function test() + private $cache; + + /** + * @Inject() + * @var CacheRedis + */ + private $redis; + + public function testCache() + { + $result = $this->cache->set('name', 'stelin'); + $name = $this->cache->get('name'); + + return [$result, $name]; + } + + public function testRedis() { - $setResult = RedisClient::set('test', 123321); - $getResult = RedisClient::get('test'); + $result = $this->redis->set('nameRedis', 'stelin2'); + $name = $this->redis->get('nameRedis'); - return [ - 'setResult' => $setResult, - 'getResult' => $getResult - ]; + return [$result, $name]; } + public function testFunc() + { + $result = cache()->set('nameFunc', 'stelin3'); + $name = cache()->get('nameFunc'); + + return [$result, $name]; + } + + public function testDelete() + { + $result = $this->cache->set('name', 'stelin'); + $del = $this->cache->delete('name'); + + return [$result, $del]; + } + + public function clear() + { + $result = $this->cache->clear(); + + return [$result]; + } + + public function setMultiple() + { + $result = $this->cache->setMultiple(['name6' => 'stelin6', 'name8' => 'stelin8']); + $ary = $this->cache->getMultiple(['name6', 'name8']); + + return [$result, $ary]; + } + + public function deleteMultiple() + { + $result = $this->cache->setMultiple(['name6' => 'stelin6', 'name8' => 'stelin8']); + $ary = $this->cache->deleteMultiple(['name6', 'name8']); + + return [$result, $ary]; + } + + public function has() + { + $result = $this->cache->set("name666", 'stelin666'); + $ret = $this->cache->has('name666'); + + return [$result, $ret]; + } + + public function testDefer() + { + $ret1 = $this->redis->deferCall('set', ['name1', 'stelin1']); + $ret2 = $this->redis->deferCall('set', ['name2', 'stelin2']); + + $r1 = $ret1->getResult(); + $r2 = $ret2->getResult(); + + $ary = $this->redis->getMultiple(['name1', 'name2']); + + return [$r1, $r2, $ary]; + } } \ No newline at end of file diff --git a/test/Web/RedisControllerTest.php b/test/Web/RedisControllerTest.php index 2eda997a..7a6dcab1 100644 --- a/test/Web/RedisControllerTest.php +++ b/test/Web/RedisControllerTest.php @@ -15,19 +15,85 @@ class RedisControllerTest extends AbstractTestCase { - /** - * @test - * @requires extension redis - * @covers \App\Controllers\RedisController - */ - public function testTest() + public function testCache() { $expected = [ - 'setResult' => true, - 'getResult' => 123321 + true, + 'stelin', ]; - $response = $this->request('GET', '/redis/test', [], parent::ACCEPT_JSON); + $response = $this->request('GET', '/redis/testCache', [], parent::ACCEPT_JSON); $response->assertSuccessful()->assertJson($expected); } + public function testRedis() + { + $expected = [ + true, + 'stelin2', + ]; + $response = $this->request('GET', '/redis/testRedis', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + + public function testFunc() + { + $expected = [ + true, + 'stelin3', + ]; + $response = $this->request('GET', '/redis/testFunc', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + + public function testDelete() + { + $expected = [ + true, + 1, + ]; + $response = $this->request('GET', '/redis/testDelete', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + + public function testClear() + { + $expected = [ + true, + ]; + $response = $this->request('GET', '/redis/clear', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + + public function testMultiple() + { + $expected = [ + true, + [ + 'stelin6', + 'stelin8', + ], + ]; + $response = $this->request('GET', '/redis/setMultiple', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + + public function testDeleteMultiple() + { + $expected = [ + true, + 2, + ]; + $response = $this->request('GET', '/redis/deleteMultiple', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + + public function testHas() + { + $expected = [ + true, + true, + ]; + $response = $this->request('GET', '/redis/has', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } } \ No newline at end of file From 0c37b2d991e46f6ef0405815b8795b25e9b8167f Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 3 Jan 2018 21:01:11 +0800 Subject: [PATCH 026/643] =?UTF-8?q?redis=E6=96=B0=E5=A2=9Edb=E9=80=89?= =?UTF-8?q?=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Tasks/TestTask.php | 6 ++---- config/properties/cache.php | 15 ++++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/Tasks/TestTask.php b/app/Tasks/TestTask.php index 9f3aa041..b6c4a943 100644 --- a/app/Tasks/TestTask.php +++ b/app/Tasks/TestTask.php @@ -10,7 +10,6 @@ use Swoft\Bean\Annotation\Inject; use Swoft\Bean\Annotation\Scheduled; use Swoft\Bean\Annotation\Task; -use Swoft\Cache\Redis\RedisClient; use Swoft\Db\EntityManager; use Swoft\Http\HttpClient; use Swoft\Service\Service; @@ -49,8 +48,7 @@ public function corTask($p1, $p2) $status++; echo "this cor task \n"; App::trace("this is task log"); - // RedisClient::set('name', 'stelin boy'); - $name = RedisClient::get('name'); + $name = cache()->get('name'); return 'cor' . " $p1" . " $p2 " . $status . " " . $name; } @@ -129,7 +127,7 @@ public function asyncTask() static $status = 1; $status++; echo "this async task \n"; - $name = RedisClient::get('name'); + $name = cache()->get('name'); App::trace("this is task log"); return 'async-' . $status . '-' . $name; } diff --git a/config/properties/cache.php b/config/properties/cache.php index 97f63a4f..6813297f 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -1,17 +1,18 @@ [ - 'name' => 'redis', + 'name' => 'redis', "uri" => [ '127.0.0.1:6379', '127.0.0.1:6379', ], - "maxIdel" => 8, - "maxActive" => 8, - "maxWait" => 8, - "timeout" => 8, - "balancer" => 'random', - "useProvider" => false, + 'maxIdel' => 8, + 'maxActive' => 8, + 'maxWait' => 8, + 'timeout' => 8, + 'balancer' => 'random', + 'useProvider' => false, 'provider' => 'consul', + 'db' => 1, ], ]; \ No newline at end of file From f9bddddb675420c118e52887b6d73ffca93aed27 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Thu, 4 Jan 2018 22:27:26 +0800 Subject: [PATCH 027/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9redis=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/RedisController.php | 15 ++++++++++++--- config/beans/base.php | 4 ++++ test/Web/RedisControllerTest.php | 11 +++++++++++ test/bootstrap.php | 6 +++--- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index e7e89a2d..cf9d23cd 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -5,8 +5,8 @@ use Swoft\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; -use Swoft\Cache\CacheInterface; use Swoft\Cache\Redis\CacheRedis; +use Swoft\Cache\Cache; /** @@ -20,8 +20,8 @@ class RedisController { /** - * @Inject() - * @var CacheInterface + * @Inject("cache") + * @var Cache */ private $cache; @@ -55,6 +55,15 @@ public function testFunc() return [$result, $name]; } + public function testFunc2() + { + $result = cache()->set('nameFunc2', 'stelin3'); + $name = cache('nameFunc2'); + $name2 = cache('nameFunc3', 'value3'); + + return [$result, $name, $name2]; + } + public function testDelete() { $result = $this->cache->set('name', 'stelin'); diff --git a/config/beans/base.php b/config/beans/base.php index 024632f0..a158ba9c 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -41,4 +41,8 @@ 'eventManager' => [ 'class' => \Swoft\Event\EventManager::class, ], + 'cache' => [ + 'class' => \Swoft\Cache\Cache::class, + 'type' => 'redis', + ] ]; diff --git a/test/Web/RedisControllerTest.php b/test/Web/RedisControllerTest.php index 7a6dcab1..bdfd684e 100644 --- a/test/Web/RedisControllerTest.php +++ b/test/Web/RedisControllerTest.php @@ -45,6 +45,17 @@ public function testFunc() $response->assertSuccessful()->assertJson($expected); } + public function testFunc2() + { + $expected = [ + true, + 'stelin3', + 'value3' + ]; + $response = $this->request('GET', '/redis/testFunc2', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + } + public function testDelete() { $expected = [ diff --git a/test/bootstrap.php b/test/bootstrap.php index 11859673..881fef0d 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -3,6 +3,8 @@ require_once dirname(dirname(__FILE__)) . '/config/define.php'; // init +\Swoft\App::$isInTest = true; + $server = new \Swoft\Server\HttpServer(); \Swoft\Bean\BeanFactory::reload([ 'application' => [ @@ -11,6 +13,4 @@ ], ]); $initApplicationContext = new \Swoft\Base\InitApplicationContext(); -$initApplicationContext->init(); - -\Swoft\App::$isInTest = true; \ No newline at end of file +$initApplicationContext->init(); \ No newline at end of file From bf6e64a820d7eff29a74ada4999edd6675f061ea Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 5 Jan 2018 14:08:14 +0800 Subject: [PATCH 028/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c272d0d4..0b572610 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Latest Version](https://camo.githubusercontent.com/4e24aee529ac200ee919e43527297e321f807f77/68747470733a2f2f706f7365722e707567782e6f72672f78636c333732312f646f72612d7270632f762f756e737461626c65)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.0.9-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.0.12-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/badge/license-apache%202.0-lightgrey.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) @@ -33,7 +33,7 @@ - 协程、异步任务投递 - 自定义用户进程 - RPC、Redis、HTTP、Mysql 协程和同步阻塞客户端无缝切换 -- Inotify 自动 Reload +- 跨平台热更新自动 Reload - 强大的日志系统 # 文档 @@ -43,11 +43,10 @@ QQ交流群:548173319 # 环境要求 -1. PHP 7.X +1. PHP 7.x 2. [Swoole 2.x](https://github.com/swoole/swoole-src/releases), 需开启协程和异步Redis 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) -5. [Inotify](https://pecl.php.net/package/inotify) (可选) # 安装 From 0a509b3f6a167bf7a9dac8133a5626b09231c1b2 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 5 Jan 2018 14:16:06 +0800 Subject: [PATCH 029/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b572610..5cd0a851 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

-[![Latest Version](https://camo.githubusercontent.com/4e24aee529ac200ee919e43527297e321f807f77/68747470733a2f2f706f7365722e707567782e6f72672f78636c333732312f646f72612d7270632f762f756e737461626c65)](https://packagist.org/packages/swoft/swoft) +[![Latest Version](https://img.shields.io/badge/unstable-v0.2.2-yellow.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.0.12-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) From 5d8cd91cc179a11cec9962beb93990d0f82ae311 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 5 Jan 2018 21:58:22 +0800 Subject: [PATCH 030/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/beans/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/beans/base.php b/config/beans/base.php index a158ba9c..022ab493 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -43,6 +43,6 @@ ], 'cache' => [ 'class' => \Swoft\Cache\Cache::class, - 'type' => 'redis', + 'driver' => 'redis', ] ]; From e5dec96bbc80346b59c758c345232143cee61f4c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 5 Jan 2018 22:48:52 +0800 Subject: [PATCH 031/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index c8022845..1f82c665 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,7 @@ +# 2018-01-05 +* 重构HttpClient +* 重构Redis +* 创建实体新增特殊变量别名 # 2017-12-26 From 5c747f314e6b5852a6eb5770e9c31c79bdc48451 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 5 Jan 2018 22:49:37 +0800 Subject: [PATCH 032/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8D=8F=E8=AE=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 8dada3ed..d1752ef5 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. From 9558e9d2bac109435f81313d1c9be2f4634519e7 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 5 Jan 2018 22:50:30 +0800 Subject: [PATCH 033/643] Add license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d1752ef5..8dada3ed 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. From af0a4be68af5249ceb1f3bd3238b55c18eedafcd Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 6 Jan 2018 17:11:20 +0800 Subject: [PATCH 034/643] =?UTF-8?q?=E6=96=B0=E5=A2=9Eenv=20demo=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 4027f193..fb5b52c6 100644 --- a/.env.example +++ b/.env.example @@ -55,6 +55,7 @@ DB_SLAVE_PROVIDER=consul # the pool of redis REDIS_NAME=redis +REDIS_DB=2 REDIS_URI=127.0.0.1:6379,127.0.0.1:6379 REDIS_MAX_IDEL=6 REDIS_MAX_ACTIVE=10 From 7dbf477b3581f4e9f80168514f8633dd2b0571b9 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 6 Jan 2018 17:33:33 +0800 Subject: [PATCH 035/643] =?UTF-8?q?Base=E7=9B=AE=E5=BD=95=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E6=88=90Core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/DemoController.php | 2 +- app/Controllers/IndexController.php | 2 +- app/Middlewares/SubMiddlewares.php | 2 +- app/Tasks/TestTask.php | 2 +- test/bootstrap.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index eff3a220..0aaf9c46 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -4,7 +4,7 @@ use App\Models\Logic\IndexLogic; use Swoft\App; -use Swoft\Base\Coroutine; +use Swoft\Core\Coroutine; use Swoft\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; use Swoft\Bean\Annotation\RequestMapping; diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index b33f6d5d..868e9934 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -158,7 +158,7 @@ public function exception() /** * @RequestMapping() * @param \Swoft\Web\Response $response - * @return \Swoft\Base\Response + * @return \Swoft\Core\Response */ public function redirect(Response $response) { diff --git a/app/Middlewares/SubMiddlewares.php b/app/Middlewares/SubMiddlewares.php index c9467985..bbd4ea2f 100644 --- a/app/Middlewares/SubMiddlewares.php +++ b/app/Middlewares/SubMiddlewares.php @@ -5,7 +5,7 @@ use Interop\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Swoft\Base\RequestHandler; +use Swoft\Core\RequestHandler; use Swoft\Bean\Annotation\Bean; use Swoft\Middleware\MiddlewareInterface; diff --git a/app/Tasks/TestTask.php b/app/Tasks/TestTask.php index b6c4a943..f062cf9e 100644 --- a/app/Tasks/TestTask.php +++ b/app/Tasks/TestTask.php @@ -6,7 +6,7 @@ use App\Models\Entity\User; use App\Models\Logic\IndexLogic; use Swoft\App; -use Swoft\Base\ApplicationContext; +use Swoft\Core\ApplicationContext; use Swoft\Bean\Annotation\Inject; use Swoft\Bean\Annotation\Scheduled; use Swoft\Bean\Annotation\Task; diff --git a/test/bootstrap.php b/test/bootstrap.php index 881fef0d..9043376a 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -12,5 +12,5 @@ 'inTest' => true ], ]); -$initApplicationContext = new \Swoft\Base\InitApplicationContext(); +$initApplicationContext = new \Swoft\Core\InitApplicationContext(); $initApplicationContext->init(); \ No newline at end of file From a276ae4c7c972edd08458a6029e32ed6d0df1e7c Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 6 Jan 2018 18:17:27 +0800 Subject: [PATCH 036/643] =?UTF-8?q?=E6=96=B0=E5=A2=9ESwoft=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Swoft.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/Swoft.php diff --git a/app/Swoft.php b/app/Swoft.php new file mode 100644 index 00000000..8092a612 --- /dev/null +++ b/app/Swoft.php @@ -0,0 +1,19 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class Swoft extends App +{ + +} \ No newline at end of file From 2f249e9fcc1facab1fdb05009a7e3bf26081796c Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 7 Jan 2018 21:27:34 +0800 Subject: [PATCH 037/643] composer.json --- composer.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 514db7d6..7ff8cded 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,10 @@ "license": "apache2.0", "require": { "php": ">=7.0", - "swoft/framework": "dev-master" + "swoft/framework": "dev-master", + "swoft/rpc": "dev-master", + "swoft/rpc-server": "dev-master", + "swoft/rpc-client": "dev-master" }, "autoload": { "classmap": [], @@ -38,6 +41,18 @@ "type": "git", "url": "https://github.com/swoft-cloud/framework" }, + "1": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc" + }, + "2": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc-server" + }, + "3": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc-client" + }, "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com/" From 9a7ec0448f68b0e45e6c4c1252f342ced3a3ee41 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Mon, 8 Jan 2018 23:43:16 +0800 Subject: [PATCH 038/643] =?UTF-8?q?=E6=8B=86=E5=88=86HttpServer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/DemoController.php | 12 ++++++------ app/Controllers/ErrorController.php | 4 ++-- app/Controllers/IndexController.php | 14 +++++++------- app/Controllers/MiddlewareController.php | 4 ++-- app/Controllers/OrmController.php | 2 +- app/Controllers/Psr7Controller.php | 6 +++--- app/Controllers/RedisController.php | 2 +- app/Controllers/RestController.php | 11 +++++------ app/Controllers/RouteController.php | 22 +++++++++++----------- app/Controllers/RpcController.php | 4 ++-- app/Controllers/ValidatorController.php | 18 +++++++++--------- composer.json | 9 +++++++-- 12 files changed, 56 insertions(+), 52 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index 0aaf9c46..b27a942e 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -5,14 +5,14 @@ use App\Models\Logic\IndexLogic; use Swoft\App; use Swoft\Core\Coroutine; -use Swoft\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; -use Swoft\Bean\Annotation\RequestMapping; -use Swoft\Bean\Annotation\RequestMethod; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\RequestMethod; use Swoft\Bean\Annotation\View; use Swoft\Task\Task; use Swoft\Web\Application; -use Swoft\Web\Request; +use Swoft\Http\Server\Http\Request; /** * 控制器demo @@ -32,7 +32,7 @@ class DemoController * * @Inject("httpRouter") * - * @var \Swoft\Router\Http\HandlerMapping + * @var \Swoft\Http\Server\Router\HandlerMapping */ private $router; @@ -59,7 +59,7 @@ class DemoController * * @RequestMapping(route="index", method={RequestMethod::GET, RequestMethod::POST}) * - * @param \Swoft\Web\Request $request + * @param Request $request * * @return array */ diff --git a/app/Controllers/ErrorController.php b/app/Controllers/ErrorController.php index 469f3270..0e83be50 100644 --- a/app/Controllers/ErrorController.php +++ b/app/Controllers/ErrorController.php @@ -3,8 +3,8 @@ namespace App\Controllers; use Swoft\App; -use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; /** * 错误控制器 diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 868e9934..48a45e6a 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -2,12 +2,12 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Bean\Annotation\View; use Swoft\Contract\Arrayable; -use Swoft\Exception\Http\BadRequestException; -use Swoft\Web\Response; +use Swoft\Http\Server\Exception\BadRequestException; +use Swoft\Http\Server\Http\Response; /** * Class IndexController @@ -103,7 +103,7 @@ public function toArray(): array /** * @RequestMapping() - * @param \Swoft\Web\Response $response + * @param Response $response * @return Response */ public function absolutePath(Response $response) @@ -157,8 +157,8 @@ public function exception() /** * @RequestMapping() - * @param \Swoft\Web\Response $response - * @return \Swoft\Core\Response + * @param Response $response + * @return Response */ public function redirect(Response $response) { diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php index 76f8e534..ff93f27f 100644 --- a/app/Controllers/MiddlewareController.php +++ b/app/Controllers/MiddlewareController.php @@ -2,10 +2,10 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Middleware; use Swoft\Bean\Annotation\Middlewares; -use Swoft\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; use App\Middlewares\GroupTestMiddleware; use App\Middlewares\ActionTestMiddleware; use App\Middlewares\SubMiddleware; diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 1a2d85d3..b2057de3 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -4,7 +4,7 @@ use App\Models\Entity\Count; use App\Models\Entity\User; -use Swoft\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Db\EntityManager; use Swoft\Db\QueryBuilder; use Swoft\Db\Types; diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index 8076b632..75933434 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -3,9 +3,9 @@ namespace App\Controllers; use Psr\Http\Message\UploadedFileInterface; -use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; -use Swoft\Web\Request; +use Swoft\Http\Server\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Http\Request; /** * @Controller(prefix="/psr7") diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index cf9d23cd..070c55d7 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -3,7 +3,7 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; use Swoft\Cache\Redis\CacheRedis; use Swoft\Cache\Cache; diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php index 75ef64d3..75a36c29 100644 --- a/app/Controllers/RestController.php +++ b/app/Controllers/RestController.php @@ -2,11 +2,10 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Ary; -use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; -use Swoft\Bean\Annotation\RequestMethod; -use Swoft\Web\Request; +use Swoft\Http\Server\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\RequestMethod; +use Swoft\Http\Server\Http\Request; /** * restful和参数验证测试demo @@ -39,7 +38,7 @@ public function list() * * @RequestMapping(route="/user", method={RequestMethod::POST,RequestMethod::PUT}) * - * @param \Swoft\Web\Request $request + * @param Request $request * * @return array */ diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 6c7eba1b..10ee66e2 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -2,10 +2,10 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; -use Swoft\Web\Request; -use Swoft\Web\Response; +use Swoft\Http\Server\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Http\Request; +use Swoft\Http\Server\Http\Response; /** * action demo @@ -32,11 +32,11 @@ public function index() * @RequestMapping(route="user/{uid}/book/{bid}/{bool}/{name}") * * @param bool $bool - * @param \Swoft\Web\Request $request + * @param Request $request * @param int $bid * @param string $name * @param int $uid - * @param \Swoft\Web\Response $response + * @param Response $response * * @return array */ @@ -57,7 +57,7 @@ public function hasNotArgs() /** * @RequestMapping(route="hasAnyArgs/{bid}") - * @param \Swoft\Web\Request $request + * @param Request $request * @param int $bid * * @return string @@ -70,7 +70,7 @@ public function hasAnyArgs(Request $request, int $bid) /** * @RequestMapping(route="hasMoreArgs") * - * @param \Swoft\Web\Request $request + * @param Request $request * @param int $bid * * @return array @@ -107,7 +107,7 @@ public function funcAnyName(string $name) } /** - * @param \Swoft\Web\Request $request + * @param Request $request * * @return array */ @@ -117,7 +117,7 @@ public function notAnnotation(Request $request) } /** - * @param \Swoft\Web\Request $request + * @param Request $request * * @return array */ @@ -127,7 +127,7 @@ public function onlyFunc(Request $request) } /** - * @param \Swoft\Web\Request $request + * @param Request $request * * @return array */ diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index fa9f1b6d..04568545 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -2,8 +2,8 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Controller; -use Swoft\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Service\Service; /** diff --git a/app/Controllers/ValidatorController.php b/app/Controllers/ValidatorController.php index 43b891b7..c7d699bb 100644 --- a/app/Controllers/ValidatorController.php +++ b/app/Controllers/ValidatorController.php @@ -2,15 +2,15 @@ namespace App\Controllers; -use Swoft\Bean\Annotation\Controller; +use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Enum; use Swoft\Bean\Annotation\Floats; use Swoft\Bean\Annotation\Integer; use Swoft\Bean\Annotation\Number; -use Swoft\Bean\Annotation\RequestMapping; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Bean\Annotation\Strings; use Swoft\Bean\Annotation\ValidatorFrom; -use Swoft\Web\Request; +use Swoft\Http\Server\Http\Request; /** * validator @@ -51,8 +51,8 @@ public function string(Request $request, string $name) * @Number(from=ValidatorFrom::POST, name="id", min=5, max=10, default=8) * @Number(from=ValidatorFrom::PATH, name="id", min=5, max=10) * - * @param \Swoft\Web\Request $request - * @param int $id + * @param Request $request + * @param int $id * * @return array */ @@ -71,8 +71,8 @@ public function number(Request $request, int $id) * @Integer(from=ValidatorFrom::POST, name="id", min=5, max=10, default=8) * @Integer(from=ValidatorFrom::PATH, name="id", min=5, max=10) * - * @param \Swoft\Web\Request $request - * @param int $id + * @param Request $request + * @param int $id * * @return array */ @@ -91,8 +91,8 @@ public function integer(Request $request, int $id) * @Floats(from=ValidatorFrom::POST, name="id", min=5.1, max=5.9, default=5.6) * @Floats(from=ValidatorFrom::PATH, name="id", min=5.1, max=5.9) * - * @param \Swoft\Web\Request $request - * @param float $id + * @param Request $request + * @param float $id * * @return array */ diff --git a/composer.json b/composer.json index 7ff8cded..772d9abe 100644 --- a/composer.json +++ b/composer.json @@ -13,7 +13,8 @@ "swoft/framework": "dev-master", "swoft/rpc": "dev-master", "swoft/rpc-server": "dev-master", - "swoft/rpc-client": "dev-master" + "swoft/rpc-client": "dev-master", + "swoft/http-server": "dev-master" }, "autoload": { "classmap": [], @@ -39,7 +40,7 @@ "repositories": { "0": { "type": "git", - "url": "https://github.com/swoft-cloud/framework" + "url": "https://github.com/stelin/framework" }, "1": { "type": "git", @@ -53,6 +54,10 @@ "type": "git", "url": "https://github.com/swoft-cloud/swoft-rpc-client" }, + "4": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-http-server" + }, "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com/" From f075755bd392a8fef4a90fd1d32e5ee2aa9be930 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 9 Jan 2018 18:44:19 +0800 Subject: [PATCH 039/643] add test requires --- test/Web/RedisControllerTest.php | 54 ++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/test/Web/RedisControllerTest.php b/test/Web/RedisControllerTest.php index bdfd684e..4e421548 100644 --- a/test/Web/RedisControllerTest.php +++ b/test/Web/RedisControllerTest.php @@ -15,7 +15,11 @@ class RedisControllerTest extends AbstractTestCase { - public function testCache() + /** + * @test + * @requires extension redis + */ + public function cache() { $expected = [ true, @@ -25,7 +29,11 @@ public function testCache() $response->assertSuccessful()->assertJson($expected); } - public function testRedis() + /** + * @test + * @requires extension redis + */ + public function redis() { $expected = [ true, @@ -35,7 +43,11 @@ public function testRedis() $response->assertSuccessful()->assertJson($expected); } - public function testFunc() + /** + * @test + * @requires extension redis + */ + public function func() { $expected = [ true, @@ -45,7 +57,11 @@ public function testFunc() $response->assertSuccessful()->assertJson($expected); } - public function testFunc2() + /** + * @test + * @requires extension redis + */ + public function func2() { $expected = [ true, @@ -56,7 +72,11 @@ public function testFunc2() $response->assertSuccessful()->assertJson($expected); } - public function testDelete() + /** + * @test + * @requires extension redis + */ + public function delete() { $expected = [ true, @@ -66,7 +86,11 @@ public function testDelete() $response->assertSuccessful()->assertJson($expected); } - public function testClear() + /** + * @test + * @requires extension redis + */ + public function clear() { $expected = [ true, @@ -75,7 +99,11 @@ public function testClear() $response->assertSuccessful()->assertJson($expected); } - public function testMultiple() + /** + * @test + * @requires extension redis + */ + public function multiple() { $expected = [ true, @@ -88,7 +116,11 @@ public function testMultiple() $response->assertSuccessful()->assertJson($expected); } - public function testDeleteMultiple() + /** + * @test + * @requires extension redis + */ + public function deleteMultiple() { $expected = [ true, @@ -98,7 +130,11 @@ public function testDeleteMultiple() $response->assertSuccessful()->assertJson($expected); } - public function testHas() + /** + * @test + * @requires extension redis + */ + public function has() { $expected = [ true, From ade726849a1f4badacf892ae94955d4b614fdf04 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 9 Jan 2018 20:48:26 +0800 Subject: [PATCH 040/643] rpc --- app/Controllers/RpcController.php | 2 +- app/Pool/UserServicePool.php | 2 +- app/Services/MiddlewareService.php | 4 ++-- app/Tasks/TestTask.php | 2 +- config/beans/base.php | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index 04568545..39c1f34e 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -4,7 +4,7 @@ use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; -use Swoft\Service\Service; +use Swoft\Rpc\Client\Service\Service; /** * rpc controller test diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index f350ac64..25d4e430 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -4,8 +4,8 @@ use Swoft\Bean\Annotation\Inject; use Swoft\Bean\Annotation\Pool; -use Swoft\Pool\ServicePool; use App\Pool\Config\UserPoolConfig; +use Swoft\Rpc\Client\Pool\ServicePool; /** * the pool of user service diff --git a/app/Services/MiddlewareService.php b/app/Services/MiddlewareService.php index 6d083101..54359fe5 100644 --- a/app/Services/MiddlewareService.php +++ b/app/Services/MiddlewareService.php @@ -4,10 +4,10 @@ use Swoft\Bean\Annotation\Middleware; use Swoft\Bean\Annotation\Middlewares; -use Swoft\Bean\Annotation\Service; -use Swoft\Bean\Annotation\Mapping; use App\Middlewares\ServiceMiddleware; use App\Middlewares\ServiceSubMiddleware; +use Swoft\Rpc\Server\Bean\Annotation\Service; +use Swoft\Rpc\Server\Bean\Annotation\Mapping; /** * the middleware of service diff --git a/app/Tasks/TestTask.php b/app/Tasks/TestTask.php index f062cf9e..f19d04e6 100644 --- a/app/Tasks/TestTask.php +++ b/app/Tasks/TestTask.php @@ -12,7 +12,7 @@ use Swoft\Bean\Annotation\Task; use Swoft\Db\EntityManager; use Swoft\Http\HttpClient; -use Swoft\Service\Service; +use Swoft\Rpc\Client\Service\Service; /** * 测试task diff --git a/config/beans/base.php b/config/beans/base.php index 022ab493..6d3b4e7f 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -2,7 +2,7 @@ return [ 'dispatcherServer' => [ - 'class' => \Swoft\Web\DispatcherServer::class, + 'class' => \Swoft\Http\Server\DispatcherServer::class, ], 'application' => [ 'id' => APP_NAME, @@ -23,13 +23,13 @@ ], ], 'httpRouter' => [ - 'class' => \Swoft\Router\Http\HandlerMapping::class, + 'class' => \Swoft\Http\Server\Router\HandlerMapping::class, 'ignoreLastSep' => false, 'tmpCacheNumber' => 1000, 'matchAll' => '', ], 'requestParser' => [ - 'class' => \Swoft\Web\RequestParser::class, + 'class' => \Swoft\Http\Server\Parser\RequestParser::class, 'parsers' => [ ], From f7cbfffc33f585f20fa20cbf814b059787945feb Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 10 Jan 2018 00:49:21 +0800 Subject: [PATCH 041/643] =?UTF-8?q?http-server=E6=B5=8B=E8=AF=95=E6=88=90?= =?UTF-8?q?=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Services/UserService.php | 4 ++-- config/beans/service.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/Services/UserService.php b/app/Services/UserService.php index e4ed4884..7ad63264 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -6,9 +6,9 @@ use Swoft\Bean\Annotation\Enum; use Swoft\Bean\Annotation\Floats; use Swoft\Bean\Annotation\Inject; -use Swoft\Bean\Annotation\Mapping; +use Swoft\Rpc\Server\Bean\Annotation\Mapping; use Swoft\Bean\Annotation\Number; -use Swoft\Bean\Annotation\Service; +use Swoft\Rpc\Server\Bean\Annotation\Service; use Swoft\Bean\Annotation\Strings; /** diff --git a/config/beans/service.php b/config/beans/service.php index 54981a01..17893e2d 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -1,13 +1,13 @@ [ - 'class' => \Swoft\Service\DispatcherService::class, + 'class' => \Swoft\Rpc\Server\DispatcherService::class, ], 'serviceRouter' => [ - 'class' => \Swoft\Router\Service\HandlerMapping::class, + 'class' => \Swoft\Rpc\Server\Router\HandlerMapping::class, ], 'servicePacker' => [ - 'class' => \Swoft\Service\ServicePacker::class, + 'class' => \Swoft\Rpc\Packer\ServicePacker::class, 'type' => 'json', 'packers' => [ From c2afb105a4d78cab4353255d69ede6c6d372eb46 Mon Sep 17 00:00:00 2001 From: Zhaohui Huang Date: Wed, 10 Jan 2018 01:26:01 +0800 Subject: [PATCH 042/643] Update .travis.yml Add PHP 7.2 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9023df1e..a714a299 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 7.0 - 7.1 + - 7.2 install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. From 68e6980a0072a65b4b6957bf5fd1d11d55678dc5 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 10 Jan 2018 22:14:09 +0800 Subject: [PATCH 043/643] rename --- app/Process/MyProcess.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Process/MyProcess.php b/app/Process/MyProcess.php index 578c7dd5..92f5b05a 100644 --- a/app/Process/MyProcess.php +++ b/app/Process/MyProcess.php @@ -3,7 +3,7 @@ namespace App\Process; use Swoft\App; -use Swoft\Process\AbstractProcess; +use Swoft\Process\AbstractProcessInterface; use Swoole\Process; /** @@ -15,7 +15,7 @@ * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ -class MyProcess extends AbstractProcess +class MyProcess extends AbstractProcessInterface { /** * 实际进程运行逻辑 From 9af18f343625a1c1c8a91444852e263bf13bd8ae Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Thu, 11 Jan 2018 23:19:41 +0800 Subject: [PATCH 044/643] =?UTF-8?q?rpc=E6=8B=86=E5=88=86ok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/RedisController.php | 4 +++- bin/bootstrap.php | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 070c55d7..9b8d1847 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -3,6 +3,8 @@ namespace App\Controllers; +use Swoft\App; +use Swoft\Console\Cbean; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; use Swoft\Cache\Redis\CacheRedis; @@ -35,7 +37,7 @@ public function testCache() { $result = $this->cache->set('name', 'stelin'); $name = $this->cache->get('name'); - + var_dump(App::getBean(Cbean::class)); return [$result, $name]; } diff --git a/bin/bootstrap.php b/bin/bootstrap.php index 77e00ccb..0ccdd532 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -1,3 +1,6 @@ Date: Fri, 12 Jan 2018 03:11:55 +0800 Subject: [PATCH 045/643] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5cd0a851..49df9e28 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ - 内置 HTTP 协程服务器 - MVC 分层设计 - 高性能路由 +- 强大的AOP - 全局容器注入 - 灵活的中间件 - 高性能 RPC From 38ef05cb0e231332689056fa99c0843f4aef3329 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 12 Jan 2018 22:29:24 +0800 Subject: [PATCH 046/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49df9e28..1d20d231 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

-[![Latest Version](https://img.shields.io/badge/unstable-v0.2.2-yellow.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) +[![Latest Version](https://img.shields.io/badge/unstable-v0.2.6-yellow.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.0.12-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) From d406e9d5d5bea8db938d9eb13ad6108bfc7ad39c Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 13 Jan 2018 10:12:08 +0800 Subject: [PATCH 047/643] component tasak --- app/Process/MyProcess.php | 2 +- app/Tasks/TestTask.php | 19 ++++++++++++------- composer.json | 8 +++++++- config/server.php | 5 ----- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/app/Process/MyProcess.php b/app/Process/MyProcess.php index 92f5b05a..e6a7c537 100644 --- a/app/Process/MyProcess.php +++ b/app/Process/MyProcess.php @@ -3,8 +3,8 @@ namespace App\Process; use Swoft\App; -use Swoft\Process\AbstractProcessInterface; use Swoole\Process; +use Swoft\Bootstrap\Process\AbstractProcessInterface; /** * 自定义进程demo diff --git a/app/Tasks/TestTask.php b/app/Tasks/TestTask.php index f19d04e6..00dbd1dc 100644 --- a/app/Tasks/TestTask.php +++ b/app/Tasks/TestTask.php @@ -6,19 +6,19 @@ use App\Models\Entity\User; use App\Models\Logic\IndexLogic; use Swoft\App; -use Swoft\Core\ApplicationContext; use Swoft\Bean\Annotation\Inject; -use Swoft\Bean\Annotation\Scheduled; -use Swoft\Bean\Annotation\Task; +use Swoft\Core\ApplicationContext; use Swoft\Db\EntityManager; -use Swoft\Http\HttpClient; +use Swoft\Http\Client; use Swoft\Rpc\Client\Service\Service; +use Swoft\Task\Bean\Annotation\Scheduled; +use Swoft\Task\Bean\Annotation\Task; /** * 测试task * - * @uses TestTask * @Task("test") + * @uses TestTask * @version 2017年09月24日 * @author stelin * @copyright Copyright 2010-2016 swoft software @@ -96,8 +96,13 @@ public function testHttp() 'desc' => 'php' ]; - $result = HttpClient::call("http://127.0.0.1/index/post?a=b", HttpClient::GET, $requestData); - $result2 = HttpClient::call("http://www.baidu.com/", HttpClient::GET, []); + $client = new Client([ + 'base_uri' => 'http://127.0.0.1/index/post?a=b', + 'timeout' => 2, + ]); + + $result = $client->post('http://127.0.0.1/index/post?a=b')->getResponse(); + $result2 = $client->get('http://www.baidu.com/'); $data['result'] = $result; $data['result2'] = $result2; return $data; diff --git a/composer.json b/composer.json index 772d9abe..99a28f62 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,8 @@ "swoft/rpc": "dev-master", "swoft/rpc-server": "dev-master", "swoft/rpc-client": "dev-master", - "swoft/http-server": "dev-master" + "swoft/http-server": "dev-master", + "swoft/task": "dev-master" }, "autoload": { "classmap": [], @@ -58,6 +59,11 @@ "type": "git", "url": "https://github.com/swoft-cloud/swoft-http-server" }, + "5": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-task" + }, + "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com/" diff --git a/config/server.php b/config/server.php index a898fde3..e751da42 100644 --- a/config/server.php +++ b/config/server.php @@ -21,11 +21,6 @@ 'model' => env('HTTP_MODEL', SWOOLE_PROCESS), 'type' => env('HTTP_TYPE', SWOOLE_SOCK_TCP), ], - 'process' => [ - 'reload' => \Swoft\Process\ReloadProcess::class, - 'cronTimer' => \Swoft\Process\CronTimerProcess::class, - 'cronExec' => \Swoft\Process\CronExecProcess::class, - ], 'crontab' => [ 'task_count' => env('CRONTAB_TASK_COUNT', 1024), 'task_queue' => env('CRONTAB_TASK_QUEUE', 2048), From 81b4c4aa80e36038298981395e6abd7529bb84ff Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 13 Jan 2018 14:55:13 +0800 Subject: [PATCH 048/643] crontable --- app/Controllers/RedisController.php | 8 +++----- app/Controllers/TaskController.php | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 app/Controllers/TaskController.php diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 9b8d1847..6c719564 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -3,12 +3,10 @@ namespace App\Controllers; -use Swoft\App; -use Swoft\Console\Cbean; -use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; -use Swoft\Cache\Redis\CacheRedis; use Swoft\Cache\Cache; +use Swoft\Cache\Redis\CacheRedis; +use Swoft\Http\Server\Bean\Annotation\Controller; /** @@ -37,7 +35,7 @@ public function testCache() { $result = $this->cache->set('name', 'stelin'); $name = $this->cache->get('name'); - var_dump(App::getBean(Cbean::class)); + return [$result, $name]; } diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php new file mode 100644 index 00000000..03a9234f --- /dev/null +++ b/app/Controllers/TaskController.php @@ -0,0 +1,23 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class TaskController +{ + public function cor() + { + $result = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_COR); + return $result; + } +} \ No newline at end of file From a11d4f568589422e6504abf1c16ffc4503804c35 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 14 Jan 2018 10:58:45 +0800 Subject: [PATCH 049/643] add excpetion --- app/Controllers/TaskController.php | 2 +- app/Exception/ExceptionHandler.php | 37 ++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 app/Exception/ExceptionHandler.php diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php index 03a9234f..d6bf4064 100644 --- a/app/Controllers/TaskController.php +++ b/app/Controllers/TaskController.php @@ -18,6 +18,6 @@ class TaskController public function cor() { $result = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_COR); - return $result; + return [$result, 1]; } } \ No newline at end of file diff --git a/app/Exception/ExceptionHandler.php b/app/Exception/ExceptionHandler.php new file mode 100644 index 00000000..4463237e --- /dev/null +++ b/app/Exception/ExceptionHandler.php @@ -0,0 +1,37 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class SwoftExceptionHandler +{ + /** + * @Handler(Exception::class) + */ + public function handlerException() + { + + } + + /** + * @Handler(RuntimeException::class) + */ + public function handlerRuntimeException() + { + + } +} \ No newline at end of file From 306b14141c2fc4ffb9149bc09edf54f7d0601a67 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 14 Jan 2018 17:05:09 +0800 Subject: [PATCH 050/643] http-message componet --- app/Controllers/DemoController.php | 4 ++-- app/Controllers/IndexController.php | 2 +- app/Controllers/Psr7Controller.php | 2 +- app/Controllers/RestController.php | 2 +- app/Controllers/RouteController.php | 4 ++-- app/Controllers/ValidatorController.php | 2 +- composer.json | 7 ++++++- 7 files changed, 14 insertions(+), 9 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index b27a942e..a390d5b8 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -5,14 +5,14 @@ use App\Models\Logic\IndexLogic; use Swoft\App; use Swoft\Core\Coroutine; -use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Bean\Annotation\Inject; +use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Http\Server\Bean\Annotation\RequestMethod; use Swoft\Bean\Annotation\View; use Swoft\Task\Task; use Swoft\Web\Application; -use Swoft\Http\Server\Http\Request; +use Swoft\Http\Message\Server\Request; /** * 控制器demo diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 48a45e6a..07c630f1 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -7,7 +7,7 @@ use Swoft\Bean\Annotation\View; use Swoft\Contract\Arrayable; use Swoft\Http\Server\Exception\BadRequestException; -use Swoft\Http\Server\Http\Response; +use Swoft\Http\Message\Server\Response; /** * Class IndexController diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index 75933434..e8a41eb3 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -5,7 +5,7 @@ use Psr\Http\Message\UploadedFileInterface; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; -use Swoft\Http\Server\Http\Request; +use Swoft\Http\Message\Server\Request; /** * @Controller(prefix="/psr7") diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php index 75a36c29..7fda0841 100644 --- a/app/Controllers/RestController.php +++ b/app/Controllers/RestController.php @@ -5,7 +5,7 @@ use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Http\Server\Bean\Annotation\RequestMethod; -use Swoft\Http\Server\Http\Request; +use Swoft\Http\Message\Server\Request; /** * restful和参数验证测试demo diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 10ee66e2..452929df 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -4,8 +4,8 @@ use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; -use Swoft\Http\Server\Http\Request; -use Swoft\Http\Server\Http\Response; +use Swoft\Http\Message\Server\Request; +use Swoft\Http\Message\Server\Response; /** * action demo diff --git a/app/Controllers/ValidatorController.php b/app/Controllers/ValidatorController.php index c7d699bb..7c2baf09 100644 --- a/app/Controllers/ValidatorController.php +++ b/app/Controllers/ValidatorController.php @@ -10,7 +10,7 @@ use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Bean\Annotation\Strings; use Swoft\Bean\Annotation\ValidatorFrom; -use Swoft\Http\Server\Http\Request; +use Swoft\Http\Message\Server\Request; /** * validator diff --git a/composer.json b/composer.json index 99a28f62..00de0d99 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ "swoft/rpc-server": "dev-master", "swoft/rpc-client": "dev-master", "swoft/http-server": "dev-master", - "swoft/task": "dev-master" + "swoft/task": "dev-master", + "swoft/http-message": "dev-master" }, "autoload": { "classmap": [], @@ -63,6 +64,10 @@ "type": "git", "url": "https://github.com/swoft-cloud/swoft-task" }, + "6": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-http-message" + }, "packagist": { "type": "composer", From a151936649d0f83dc959560eed62b1dba268a2ef Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Mon, 15 Jan 2018 23:34:00 +0800 Subject: [PATCH 051/643] =?UTF-8?q?=E8=A7=86=E5=9B=BE=E6=8B=86=E5=88=86ok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/DemoController.php | 2 +- app/Controllers/IndexController.php | 39 ++++++++++++++++++++++++++++- composer.json | 7 +++++- config/beans/base.php | 7 ++++-- resources/views/layouts/default.php | 2 +- 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index a390d5b8..93311346 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -9,7 +9,7 @@ use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Http\Server\Bean\Annotation\RequestMethod; -use Swoft\Bean\Annotation\View; +use Swoft\View\Bean\Annotation\View; use Swoft\Task\Task; use Swoft\Web\Application; use Swoft\Http\Message\Server\Request; diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 07c630f1..50832d20 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -4,7 +4,7 @@ use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; -use Swoft\Bean\Annotation\View; +use Swoft\View\Bean\Annotation\View; use Swoft\Contract\Arrayable; use Swoft\Http\Server\Exception\BadRequestException; use Swoft\Http\Message\Server\Response; @@ -56,6 +56,43 @@ public function index() return compact('name', 'notes', 'links'); } + /** + * show view by view function + */ + public function templateView() + { + $name = 'Swoft View'; + $notes = [ + 'New Generation of PHP Framework', + 'Hign Performance, Coroutine and Full Stack' + ]; + $links = [ + [ + 'name' => 'Home', + 'link' => 'http://www.swoft.org/', + ], + [ + 'name' => 'Documentation', + 'link' => 'http://doc.swoft.org/', + ], + [ + 'name' => 'Case', + 'link' => 'http://swoft.org/case', + ], + [ + 'name' => 'Issue', + 'link' => 'https://github.com/swoft-cloud/swoft/issues', + ], + [ + 'name' => 'GitHub', + 'link' => 'https://github.com/swoft-cloud/swoft', + ], + ]; + $data = compact('name', 'notes', 'links'); + + return view('index/index', $data); + } + /** * @RequestMapping() * @View(template="index/index") diff --git a/composer.json b/composer.json index 00de0d99..69aab414 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "swoft/rpc-client": "dev-master", "swoft/http-server": "dev-master", "swoft/task": "dev-master", - "swoft/http-message": "dev-master" + "swoft/http-message": "dev-master", + "swoft/view": "dev-master" }, "autoload": { "classmap": [], @@ -68,6 +69,10 @@ "type": "git", "url": "https://github.com/swoft-cloud/swoft-http-message" }, + "7": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-view" + }, "packagist": { "type": "composer", diff --git a/config/beans/base.php b/config/beans/base.php index 6d3b4e7f..d6827707 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -3,6 +3,9 @@ return [ 'dispatcherServer' => [ 'class' => \Swoft\Http\Server\DispatcherServer::class, + 'middlewares' => [ + \Swoft\View\Middleware\ViewMiddleware::class + ] ], 'application' => [ 'id' => APP_NAME, @@ -34,8 +37,8 @@ ], ], - 'renderer' => [ - 'class' => \Swoft\Web\ViewRenderer::class, + 'view' => [ + 'class' => \Swoft\View\Base\View::class, 'viewsPath' => '@resources/views/', ], 'eventManager' => [ diff --git a/resources/views/layouts/default.php b/resources/views/layouts/default.php index c1cce8e3..866e894f 100644 --- a/resources/views/layouts/default.php +++ b/resources/views/layouts/default.php @@ -1,6 +1,6 @@ From 32f5217792d6a06663a534e55c80cf0001de4abb Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 16 Jan 2018 09:57:26 +0800 Subject: [PATCH 052/643] =?UTF-8?q?=E6=8B=86=E5=88=86=E8=A7=86=E5=9B=BEok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Controllers/DemoController.php | 2 +- app/Controllers/IndexController.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index 93311346..963900e0 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -11,7 +11,7 @@ use Swoft\Http\Server\Bean\Annotation\RequestMethod; use Swoft\View\Bean\Annotation\View; use Swoft\Task\Task; -use Swoft\Web\Application; +use Swoft\Core\Application; use Swoft\Http\Message\Server\Request; /** diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 50832d20..1ce5af17 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -140,13 +140,11 @@ public function toArray(): array /** * @RequestMapping() - * @param Response $response * @return Response */ - public function absolutePath(Response $response) + public function absolutePath() { - $template = '@res/views/index/index.php'; - return $response->view([ + $data = [ 'name' => 'Swoft', 'notes' => ['New Generation of PHP Framework', 'Hign Performance, Coroutine and Full Stack'], 'links' => [ @@ -171,7 +169,9 @@ public function absolutePath(Response $response) 'link' => 'https://github.com/swoft-cloud/swoft', ], ] - ], $template); + ]; + $template = '@res/views/index/index.php'; + return view($template, $data); } /** From 41a7bd4f4b26bbfbffffd988b796bfab30961231 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 17 Jan 2018 21:11:57 +0800 Subject: [PATCH 053/643] exception --- app/Controllers/ExceptionController.php | 48 +++++++++++++++++++++ app/Exception/ExceptionHandler.php | 37 ---------------- app/Exception/SwoftExceptionHandler.php | 56 +++++++++++++++++++++++++ config/properties/app.php | 1 + 4 files changed, 105 insertions(+), 37 deletions(-) create mode 100644 app/Controllers/ExceptionController.php delete mode 100644 app/Exception/ExceptionHandler.php create mode 100644 app/Exception/SwoftExceptionHandler.php diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php new file mode 100644 index 00000000..223ef375 --- /dev/null +++ b/app/Controllers/ExceptionController.php @@ -0,0 +1,48 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class ExceptionController +{ + /** + * @RequestMapping() + * @throws \Exception + */ + public function exceptioin() + { + throw new \Exception("this is exception"); + } + + /** + * @RequestMapping() + * @throws RuntimeException + */ + public function runtimeException() + { + throw new RuntimeException("my exception"); + } + + /** + * @RequestMapping() + * @throws ValidatorException + */ + public function defaultException() + { + throw new ValidatorException("validator exception! "); + } +} \ No newline at end of file diff --git a/app/Exception/ExceptionHandler.php b/app/Exception/ExceptionHandler.php deleted file mode 100644 index 4463237e..00000000 --- a/app/Exception/ExceptionHandler.php +++ /dev/null @@ -1,37 +0,0 @@ - - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class SwoftExceptionHandler -{ - /** - * @Handler(Exception::class) - */ - public function handlerException() - { - - } - - /** - * @Handler(RuntimeException::class) - */ - public function handlerRuntimeException() - { - - } -} \ No newline at end of file diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php new file mode 100644 index 00000000..b0843d74 --- /dev/null +++ b/app/Exception/SwoftExceptionHandler.php @@ -0,0 +1,56 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class SwoftExceptionHandler +{ + /** + * @Handler(Exception::class) + * + * @param Response $response + * @param \Throwable $throwable + * + * @return Response + */ + public function handlerException(Response $response, \Throwable $throwable) + { + $file = $throwable->getFile(); + $code = $throwable->getCode(); + $exception = $throwable->getMessage(); + + return $response->json([$exception, $file, $code]); + } + + /** + * @Handler(RuntimeException::class) + * + * @param Response $response + * @param \Throwable $throwable + * + * @return Response + */ + public function handlerRuntimeException(Response $response, \Throwable $throwable) + { + $file = $throwable->getFile(); + $code = $throwable->getCode(); + $exception = $throwable->getMessage(); + + return $response->json([$exception, 'runtimeException']); + } +} \ No newline at end of file diff --git a/config/properties/app.php b/config/properties/app.php index d7d73701..ab5b560c 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -11,6 +11,7 @@ 'App\Process', 'App\Breaker', 'App\Pool', + 'App\Exception', ], 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', From f73500dc0a440b182c473e699d9b7318d0ced0e8 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 17 Jan 2018 21:42:07 +0800 Subject: [PATCH 054/643] add view exception --- app/Controllers/ExceptionController.php | 10 +++ app/Exception/SwoftExceptionHandler.php | 54 ++++++++++++-- resources/views/exception/index.php | 96 +++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 resources/views/exception/index.php diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php index 223ef375..59ba8af2 100644 --- a/app/Controllers/ExceptionController.php +++ b/app/Controllers/ExceptionController.php @@ -2,6 +2,7 @@ namespace App\Controllers; +use Swoft\Exception\BadMethodCallException; use Swoft\Exception\RuntimeException; use Swoft\Exception\ValidatorException; use Swoft\Http\Server\Bean\Annotation\Controller; @@ -45,4 +46,13 @@ public function defaultException() { throw new ValidatorException("validator exception! "); } + + /** + * @RequestMapping() + * @throws BadMethodCallException + */ + public function viewException() + { + throw new BadMethodCallException("view exception! "); + } } \ No newline at end of file diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index b0843d74..29703484 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -6,7 +6,9 @@ use Swoft\Bean\Annotation\Handler; use Swoft\Exception\RuntimeException; use Exception; +use Swoft\Http\Message\Server\Request; use Swoft\Http\Message\Server\Response; +use Swoft\Exception\BadMethodCallException; /** * the handler of global exception @@ -30,8 +32,8 @@ class SwoftExceptionHandler */ public function handlerException(Response $response, \Throwable $throwable) { - $file = $throwable->getFile(); - $code = $throwable->getCode(); + $file = $throwable->getFile(); + $code = $throwable->getCode(); $exception = $throwable->getMessage(); return $response->json([$exception, $file, $code]); @@ -47,10 +49,54 @@ public function handlerException(Response $response, \Throwable $throwable) */ public function handlerRuntimeException(Response $response, \Throwable $throwable) { - $file = $throwable->getFile(); - $code = $throwable->getCode(); + $file = $throwable->getFile(); + $code = $throwable->getCode(); $exception = $throwable->getMessage(); return $response->json([$exception, 'runtimeException']); } + + /** + * @Handler(BadMethodCallException::class) + * + * @param Request $request + * @param Response $response + * @param \Throwable $throwable + * + * @return Response + */ + public function handlerViewException(Request $request, Response $response, \Throwable $throwable) + { + $name = $throwable->getMessage(). $request->getUri()->getPath(); + $notes = [ + 'New Generation of PHP Framework', + 'Hign Performance, Coroutine and Full Stack', + ]; + $links = [ + [ + 'name' => 'Home', + 'link' => 'http://www.swoft.org/', + ], + [ + 'name' => 'Documentation', + 'link' => 'http://doc.swoft.org/', + ], + [ + 'name' => 'Case', + 'link' => 'http://swoft.org/case', + ], + [ + 'name' => 'Issue', + 'link' => 'https://github.com/swoft-cloud/swoft/issues', + ], + [ + 'name' => 'GitHub', + 'link' => 'https://github.com/swoft-cloud/swoft', + ], + ]; + $data = compact('name', 'notes', 'links'); + + return view('exception/index', $data); + } + } \ No newline at end of file diff --git a/resources/views/exception/index.php b/resources/views/exception/index.php new file mode 100644 index 00000000..82f8ce7e --- /dev/null +++ b/resources/views/exception/index.php @@ -0,0 +1,96 @@ + + + + + + + + <?= $name ?> + + + + + + + + +
+ +
+
+ +
+ + +
+ +
+ + + +
+
+ + \ No newline at end of file From 22aab3cc9922089d897231a9eae3f5700fb6873e Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Thu, 18 Jan 2018 20:43:03 +0800 Subject: [PATCH 055/643] cahce and redis --- app/Controllers/RedisController.php | 4 ++-- composer.json | 18 ++++++++++++++++-- config/beans/base.php | 3 +++ 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 6c719564..4dd10ae1 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -5,7 +5,7 @@ use Swoft\Bean\Annotation\Inject; use Swoft\Cache\Cache; -use Swoft\Cache\Redis\CacheRedis; +use Swoft\Redis\RedisCache; use Swoft\Http\Server\Bean\Annotation\Controller; @@ -27,7 +27,7 @@ class RedisController /** * @Inject() - * @var CacheRedis + * @var RedisCache */ private $redis; diff --git a/composer.json b/composer.json index 69aab414..ca489cb9 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,10 @@ "swoft/http-server": "dev-master", "swoft/task": "dev-master", "swoft/http-message": "dev-master", - "swoft/view": "dev-master" + "swoft/view": "dev-master", + "swoft/db": "dev-master", + "swoft/cache": "dev-master", + "swoft/redis": "dev-master" }, "autoload": { "classmap": [], @@ -73,7 +76,18 @@ "type": "git", "url": "https://github.com/swoft-cloud/swoft-view" }, - + "8": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-db" + }, + "9": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-cache" + }, + "10": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-redis" + }, "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com/" diff --git a/config/beans/base.php b/config/beans/base.php index d6827707..df89643c 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -47,5 +47,8 @@ 'cache' => [ 'class' => \Swoft\Cache\Cache::class, 'driver' => 'redis', + 'drivers' => [ + 'redis' => \Swoft\Redis\RedisCache::class + ] ] ]; From 51566f01a5bc540c6a1518004e0f50b964ad742c Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Thu, 18 Jan 2018 23:41:29 +0800 Subject: [PATCH 056/643] db --- app/Models/Entity/Count.php | 8 ++++---- app/Models/Entity/User.php | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/Models/Entity/Count.php b/app/Models/Entity/Count.php index a55ba247..f91bac52 100644 --- a/app/Models/Entity/Count.php +++ b/app/Models/Entity/Count.php @@ -2,10 +2,10 @@ namespace App\Models\Entity; -use Swoft\Bean\Annotation\Column; -use Swoft\Bean\Annotation\Entity; -use Swoft\Bean\Annotation\Id; -use Swoft\Bean\Annotation\Table; +use Swoft\Db\Bean\Annotation\Column; +use Swoft\Db\Bean\Annotation\Entity; +use Swoft\Db\Bean\Annotation\Id; +use Swoft\Db\Bean\Annotation\Table; use Swoft\Db\Model; use Swoft\Db\Types; diff --git a/app/Models/Entity/User.php b/app/Models/Entity/User.php index dfe235f2..65695418 100644 --- a/app/Models/Entity/User.php +++ b/app/Models/Entity/User.php @@ -2,13 +2,12 @@ namespace App\Models\Entity; +use Swoft\Db\Bean\Annotation\Id; +use Swoft\Db\Bean\Annotation\Required; +use Swoft\Db\Bean\Annotation\Table; +use Swoft\Db\Bean\Annotation\Column; +use Swoft\Db\Bean\Annotation\Entity; use Swoft\Db\Model; -use Swoft\Bean\Annotation\Column; -use Swoft\Bean\Annotation\Entity; -use Swoft\Bean\Annotation\Enum; -use Swoft\Bean\Annotation\Id; -use Swoft\Bean\Annotation\Required; -use Swoft\Bean\Annotation\Table; use Swoft\Db\Types; /** From 7e90701873c3641b5ffacc17a16ae0ac2325f211 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Sun, 21 Jan 2018 01:35:05 +0800 Subject: [PATCH 057/643] update composer.json --- composer.json | 72 +++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/composer.json b/composer.json index ca489cb9..6e9e92b5 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "license": "apache2.0", "require": { "php": ">=7.0", - "swoft/framework": "dev-master", + "swoft/framework": "dev-component", "swoft/rpc": "dev-master", "swoft/rpc-server": "dev-master", "swoft/rpc-client": "dev-master", @@ -43,54 +43,54 @@ "eaglewu/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^5.7" }, - "repositories": { - "0": { + "repositories": [ + { "type": "git", - "url": "https://github.com/stelin/framework" + "url": "https://github.com/swoft-cloud/framework" }, - "1": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc" }, - "2": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc-server" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc-server" }, - "3": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc-client" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc-client" }, - "4": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-http-server" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-http-server" }, - "5": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-task" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-task" }, - "6": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-http-message" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-http-message" }, - "7": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-view" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-view" }, - "8": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-db" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-db" }, - "9": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-cache" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-cache" }, - "10": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-redis" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-redis" }, - "packagist": { + { "type": "composer", "url": "https://packagist.phpcomposer.com/" } - } + ] } From d118aa8346e0cefe1a23241b3aacf997e097cc4f Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 23 Jan 2018 22:02:51 +0800 Subject: [PATCH 058/643] console --- app/Commands/TestCommand.php | 80 +++++++++++++++++++++++++++++ app/Commands/TestController.php | 49 ------------------ app/Controllers/RouteController.php | 2 +- app/Controllers/RpcController.php | 2 +- composer.json | 8 ++- 5 files changed, 89 insertions(+), 52 deletions(-) create mode 100644 app/Commands/TestCommand.php delete mode 100644 app/Commands/TestController.php diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php new file mode 100644 index 00000000..5354a119 --- /dev/null +++ b/app/Commands/TestCommand.php @@ -0,0 +1,80 @@ + + * @copyright Copyright 2010-2016 swoft software + * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + */ +class TestCommand +{ + /** + * this test command + * + * @Usage + * test:{command} [arguments] [options] + * + * @Options + * -o,--o this is command option + * + * @Arguments + * arg this is argument + * + * @Example + * php swoft test:test arg=stelin -o opt + * + * @param Input $input + * @param Output $output + * + * @Mapping() + */ + public function test(Input $input, Output $output) + { + var_dump('test', $input, $output); + } + + /** + * this demo command + * + * @Usage + * test:{command} [arguments] [options] + * + * @Options + * -o,--o this is command option + * + * @Arguments + * arg this is argument + * + * @Example + * php swoft test:demo arg=stelin -o opt + * + * @Mapping() + */ + public function demo() + { + $hasOpt = input()->hasOpt('o'); + $opt = input()->getOpt('o'); + $name = input()->getArg('arg', 'swoft'); + + App::trace("this is command log"); + Log::info("this is comamnd info log"); + /* @var UserLogic $logic */ + $logic = App::getBean(UserLogic::class); + $data = $logic->getUserInfo(['uid1']); + var_dump($hasOpt, $opt, $name, $data); + } +} \ No newline at end of file diff --git a/app/Commands/TestController.php b/app/Commands/TestController.php deleted file mode 100644 index 92102b36..00000000 --- a/app/Commands/TestController.php +++ /dev/null @@ -1,49 +0,0 @@ - - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class TestController extends ConsoleController -{ - /** - * this demo command - * - * @usage - * server:{command} [arguments] [options] - * - * @options - * -o,--o this is command option - * - * @arguments - * arg this is argument - * - * @example - * php swoft test:demo arg=stelin -o opt - */ - public function demoCommand() - { - $hasOpt = $this->input->hasOpt('o'); - $opt = $this->input->getOpt('o'); - $name = $this->input->getArg('arg', 'swoft'); - - App::trace("this is command log"); - Log::info("this is comamnd info log"); - /* @var UserLogic $logic*/ - $logic = App::getBean(UserLogic::class); - $data = $logic->getUserInfo(['uid1']); - var_dump($hasOpt, $opt, $name, $data); - } -} \ No newline at end of file diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 452929df..85c0d42a 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -12,7 +12,7 @@ * * @Controller(prefix="/route") * - * @uses TestController + * @uses TestCommand * @version 2017年11月26日 * @author stelin * @copyright Copyright 2010-2016 swoft software diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index 39c1f34e..1e46021f 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -10,7 +10,7 @@ * rpc controller test * * @Controller(prefix="rpc") - * @uses RpcController + * @uses RpcCommand * @version 2017年11月27日 * @author stelin * @copyright Copyright 2010-2016 swoft software diff --git a/composer.json b/composer.json index ca489cb9..608dfa2d 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,8 @@ "swoft/view": "dev-master", "swoft/db": "dev-master", "swoft/cache": "dev-master", - "swoft/redis": "dev-master" + "swoft/redis": "dev-master", + "swoft/console": "dev-master" }, "autoload": { "classmap": [], @@ -88,6 +89,11 @@ "type": "git", "url": "https://github.com/swoft-cloud/swoft-redis" }, + "11": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-console" + }, + "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com/" From 72e4dcf7e486e665bb15239e865452cfa869ac76 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 23 Jan 2018 23:27:05 +0800 Subject: [PATCH 059/643] Compatible PSR15 --- app/Middlewares/ActionTestMiddleware.php | 4 +-- app/Middlewares/ControlerSubMiddleware.php | 4 +-- app/Middlewares/ControlerTestMiddleware.php | 4 +-- app/Middlewares/GroupTestMiddleware.php | 4 +-- app/Middlewares/ServiceMiddleware.php | 4 +-- app/Middlewares/ServiceSubMiddleware.php | 4 +-- app/Middlewares/SubMiddleware.php | 4 +-- app/Middlewares/SubMiddlewares.php | 4 +-- composer.json | 38 ++++++++++++--------- 9 files changed, 37 insertions(+), 33 deletions(-) diff --git a/app/Middlewares/ActionTestMiddleware.php b/app/Middlewares/ActionTestMiddleware.php index ad6d5bfe..5275daba 100644 --- a/app/Middlewares/ActionTestMiddleware.php +++ b/app/Middlewares/ActionTestMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -25,7 +25,7 @@ class ActionTestMiddleware implements MiddlewareInterface * response creation to a handler. * * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/app/Middlewares/ControlerSubMiddleware.php b/app/Middlewares/ControlerSubMiddleware.php index fbcbe7cb..0943a322 100644 --- a/app/Middlewares/ControlerSubMiddleware.php +++ b/app/Middlewares/ControlerSubMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -22,7 +22,7 @@ class ControlerSubMiddleware implements MiddlewareInterface { /** * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * * @return \Psr\Http\Message\ResponseInterface */ diff --git a/app/Middlewares/ControlerTestMiddleware.php b/app/Middlewares/ControlerTestMiddleware.php index 88437834..36ebbbce 100644 --- a/app/Middlewares/ControlerTestMiddleware.php +++ b/app/Middlewares/ControlerTestMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -22,7 +22,7 @@ class ControlerTestMiddleware implements MiddlewareInterface { /** * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * * @return \Psr\Http\Message\ResponseInterface */ diff --git a/app/Middlewares/GroupTestMiddleware.php b/app/Middlewares/GroupTestMiddleware.php index 17c1dd8a..efdbe9c6 100644 --- a/app/Middlewares/GroupTestMiddleware.php +++ b/app/Middlewares/GroupTestMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -25,7 +25,7 @@ class GroupTestMiddleware implements MiddlewareInterface * response creation to a handler. * * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/app/Middlewares/ServiceMiddleware.php b/app/Middlewares/ServiceMiddleware.php index 55c38e3a..4e1e9e30 100644 --- a/app/Middlewares/ServiceMiddleware.php +++ b/app/Middlewares/ServiceMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -22,7 +22,7 @@ class ServiceMiddleware implements MiddlewareInterface { /** * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * * @return \Psr\Http\Message\ResponseInterface */ diff --git a/app/Middlewares/ServiceSubMiddleware.php b/app/Middlewares/ServiceSubMiddleware.php index 46d9167c..0e003c0f 100644 --- a/app/Middlewares/ServiceSubMiddleware.php +++ b/app/Middlewares/ServiceSubMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -22,7 +22,7 @@ class ServiceSubMiddleware implements MiddlewareInterface { /** * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * * @return \Psr\Http\Message\ResponseInterface */ diff --git a/app/Middlewares/SubMiddleware.php b/app/Middlewares/SubMiddleware.php index e6770cf2..cd6e13f1 100644 --- a/app/Middlewares/SubMiddleware.php +++ b/app/Middlewares/SubMiddleware.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Bean\Annotation\Bean; @@ -25,7 +25,7 @@ class SubMiddleware implements MiddlewareInterface * response creation to a handler. * * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/app/Middlewares/SubMiddlewares.php b/app/Middlewares/SubMiddlewares.php index bbd4ea2f..f689decb 100644 --- a/app/Middlewares/SubMiddlewares.php +++ b/app/Middlewares/SubMiddlewares.php @@ -2,7 +2,7 @@ namespace App\Middlewares; -use Interop\Http\Server\RequestHandlerInterface; +use Psr\Http\Server\RequestHandlerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Swoft\Core\RequestHandler; @@ -26,7 +26,7 @@ class SubMiddlewares implements MiddlewareInterface * response creation to a handler. * * @param \Psr\Http\Message\ServerRequestInterface $request - * @param \Interop\Http\Server\RequestHandlerInterface $handler + * @param \Psr\Http\Server\RequestHandlerInterface $handler * @return \Psr\Http\Message\ResponseInterface */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface diff --git a/composer.json b/composer.json index 8c0c4e2b..2e5b9cd1 100644 --- a/composer.json +++ b/composer.json @@ -44,58 +44,62 @@ "eaglewu/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^5.7" }, - "repositories": [ - { + "repositories": { + "0": { "type": "git", "url": "https://github.com/swoft-cloud/framework" }, - { + "1": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-rpc" }, - { + "2": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-rpc-server" }, - { + "3": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-rpc-client" }, - { + "4": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-http-server" }, - { + "5": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-task" }, - { + "6": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-http-message" }, - { + "7": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-view" }, - { + "8": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-db" }, - { + "9": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-cache" }, - { + "10": { "type": "git", "url": "https://github.com/swoft-cloud/swoft-redis" }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-console" + "11": { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-console" + }, + "12": { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" }, - { + "packagist": { "type": "composer", "url": "https://packagist.phpcomposer.com/" } - ] + } } From e7a221bd480eec519bbcc6f3db2d5673fdfd6a36 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 24 Jan 2018 12:55:10 +0800 Subject: [PATCH 060/643] recover phpunit --- app/Commands/TestCommand.php | 2 +- app/Controllers/IndexController.php | 2 +- app/Exception/SwoftExceptionHandler.php | 20 +++++++++++++++++++- test/Web/AbstractTestCase.php | 3 +-- test/Web/IndexControllerTest.php | 9 ++++++++- test/Web/RouteTest.php | 18 ------------------ test/bootstrap.php | 5 ++++- 7 files changed, 34 insertions(+), 25 deletions(-) diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php index 9a9518ac..991d85bc 100644 --- a/app/Commands/TestCommand.php +++ b/app/Commands/TestCommand.php @@ -13,7 +13,7 @@ /** * the group of test command * - * @Command(coroutine=false) + * @Command() * @uses TestCommand * @version 2017年11月03日 * @author stelin diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 1ce5af17..43a1811a 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -170,7 +170,7 @@ public function absolutePath() ], ] ]; - $template = '@res/views/index/index.php'; + $template = 'index/index'; return view($template, $data); } diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index 29703484..ec484286 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -9,6 +9,7 @@ use Swoft\Http\Message\Server\Request; use Swoft\Http\Message\Server\Response; use Swoft\Exception\BadMethodCallException; +use Swoft\Exception\ValidatorException; /** * the handler of global exception @@ -33,10 +34,12 @@ class SwoftExceptionHandler public function handlerException(Response $response, \Throwable $throwable) { $file = $throwable->getFile(); + $line = $throwable->getLine(); $code = $throwable->getCode(); $exception = $throwable->getMessage(); - return $response->json([$exception, $file, $code]); + $data = ['msg' => $exception, 'file' => $file, 'line' => $line, 'code' => $code]; + return $response->json($data); } /** @@ -56,6 +59,21 @@ public function handlerRuntimeException(Response $response, \Throwable $throwabl return $response->json([$exception, 'runtimeException']); } + /** + * @Handler(ValidatorException::class) + * + * @param Response $response + * @param \Throwable $throwable + * + * @return Response + */ + public function handlerValidatorException(Response $response, \Throwable $throwable) + { + $exception = $throwable->getMessage(); + + return $response->json(["message" => $exception]); + } + /** * @Handler(BadMethodCallException::class) * diff --git a/test/Web/AbstractTestCase.php b/test/Web/AbstractTestCase.php index 4b36e1ca..e4524220 100644 --- a/test/Web/AbstractTestCase.php +++ b/test/Web/AbstractTestCase.php @@ -3,7 +3,6 @@ namespace Swoft\Test\Web; -use Swoft\App; use Swoft\Helper\ArrayHelper; use Swoft\Testing\SwooleRequest as TestSwooleRequest; use Swoft\Testing\SwooleResponse as TestSwooleResponse; @@ -39,7 +38,7 @@ public function request($method, $uri, $parameters = [], $accept = self::ACCEPT_ $swooleRequest = new TestSwooleRequest(); $swooleRequest->setRawContent($rawContent); $this->buildMockRequest($method, $uri, $parameters, $accept, $swooleRequest, $headers); - return App::getDispatcherServer()->doDispatcher($swooleRequest, $swooleResponse);; + return dispatcher_server()->doDispatcher($swooleRequest, $swooleResponse);; } /** diff --git a/test/Web/IndexControllerTest.php b/test/Web/IndexControllerTest.php index 5f14a8ca..2a3fbe60 100644 --- a/test/Web/IndexControllerTest.php +++ b/test/Web/IndexControllerTest.php @@ -99,8 +99,15 @@ public function testIndex() */ public function testException() { + $data = [ + 'msg' => '', + 'file' => '/home/worker/data/www/swoft/app/Controllers/IndexController.php', + 'line' => 192, + 'code' => 400, + ]; + $response = $this->request('GET', '/index/exception', [], parent::ACCEPT_JSON); - $response->assertStatus(400)->assertJson(['message' => 'Bad Request']); + $response->assertJson($data); } /** diff --git a/test/Web/RouteTest.php b/test/Web/RouteTest.php index ccf6c565..7ead8a0d 100644 --- a/test/Web/RouteTest.php +++ b/test/Web/RouteTest.php @@ -30,24 +30,6 @@ public function testFuncArgs() $response->assertExactJson($data); } - /** - * @covers closure route - */ - public function testClosureFuncArgs() - { - $data = [ - 'clouse', - 456, - 123, - true, - "test", - "Swoft\\Testing\\Web\\Request", - "Swoft\\Testing\\Web\\Response", - ]; - $response = $this->request('GET', '/user/123/book/456/1/test', [], parent::ACCEPT_JSON); - $response->assertExactJson($data); - } - /** * @covers \App\Controllers\RouteController::hasNotArgs */ diff --git a/test/bootstrap.php b/test/bootstrap.php index 9043376a..9c77660e 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -5,12 +5,15 @@ // init \Swoft\App::$isInTest = true; -$server = new \Swoft\Server\HttpServer(); +\Swoft\Bean\BeanFactory::init(); \Swoft\Bean\BeanFactory::reload([ 'application' => [ 'class' => \Swoft\Testing\Application::class, 'inTest' => true ], ]); + +$server = new \Swoft\Http\Server\Http\HttpServer(); + $initApplicationContext = new \Swoft\Core\InitApplicationContext(); $initApplicationContext->init(); \ No newline at end of file From c4dd52afb3ad0f12dffd4e6e8454367a42b88bd7 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 24 Jan 2018 13:11:05 +0800 Subject: [PATCH 061/643] recover phpunit --- app/Controllers/IndexController.php | 2 +- app/Exception/SwoftExceptionHandler.php | 16 ++++++++++++++++ test/Web/IndexControllerTest.php | 5 +---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 43a1811a..2b1509e4 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -189,7 +189,7 @@ public function raw() */ public function exception() { - throw new BadRequestException(); + throw new BadRequestException("bad request exception"); } /** diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index ec484286..31cb08fd 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -10,6 +10,7 @@ use Swoft\Http\Message\Server\Response; use Swoft\Exception\BadMethodCallException; use Swoft\Exception\ValidatorException; +use Swoft\Http\Server\Exception\BadRequestException; /** * the handler of global exception @@ -74,6 +75,21 @@ public function handlerValidatorException(Response $response, \Throwable $throwa return $response->json(["message" => $exception]); } + /** + * @Handler(BadRequestException::class) + * + * @param Response $response + * @param \Throwable $throwable + * + * @return Response + */ + public function handlerBadRequestException(Response $response, \Throwable $throwable) + { + $exception = $throwable->getMessage(); + + return $response->json(["message" => $exception]); + } + /** * @Handler(BadMethodCallException::class) * diff --git a/test/Web/IndexControllerTest.php b/test/Web/IndexControllerTest.php index 2a3fbe60..e419d6d7 100644 --- a/test/Web/IndexControllerTest.php +++ b/test/Web/IndexControllerTest.php @@ -100,10 +100,7 @@ public function testIndex() public function testException() { $data = [ - 'msg' => '', - 'file' => '/home/worker/data/www/swoft/app/Controllers/IndexController.php', - 'line' => 192, - 'code' => 400, + 'message' => 'bad request exception' ]; $response = $this->request('GET', '/index/exception', [], parent::ACCEPT_JSON); From b5d55ca332e23d720fe649a27c7fe370b5486081 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 27 Jan 2018 11:42:50 +0800 Subject: [PATCH 062/643] recover unit --- app/Pool/Config/UserPoolConfig.php | 4 +-- config/beans/service.php | 2 +- test/AbstractTestCase.php | 17 ------------ test/{Web => Cases}/AbstractTestCase.php | 27 ++++++++++++------- test/{Web => Cases}/DemoControllerTest.php | 4 +-- test/{Web => Cases}/IndexControllerTest.php | 3 +-- test/{Web => Cases}/MiddlewareTest.php | 2 +- test/{Web => Cases}/RedisControllerTest.php | 7 ++--- test/{Web => Cases}/RestTest.php | 2 +- test/{Web => Cases}/RouteTest.php | 2 +- .../ValidatorControllerTest.php | 2 +- 11 files changed, 28 insertions(+), 44 deletions(-) delete mode 100644 test/AbstractTestCase.php rename test/{Web => Cases}/AbstractTestCase.php (87%) rename test/{Web => Cases}/DemoControllerTest.php (95%) rename test/{Web => Cases}/IndexControllerTest.php (99%) rename test/{Web => Cases}/MiddlewareTest.php (98%) rename test/{Web => Cases}/RedisControllerTest.php (97%) rename test/{Web => Cases}/RestTest.php (99%) rename test/{Web => Cases}/RouteTest.php (99%) rename test/{Web => Cases}/ValidatorControllerTest.php (99%) diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php index 8622f106..644d58be 100644 --- a/app/Pool/Config/UserPoolConfig.php +++ b/app/Pool/Config/UserPoolConfig.php @@ -5,8 +5,8 @@ use Swoft\Bean\Annotation\Bean; use Swoft\Bean\Annotation\Value; use Swoft\Pool\BalancerSelector; +use Swoft\Pool\PoolProperties; use Swoft\Pool\ProviderSelector; -use Swoft\Testing\Pool\Config\PropertyPoolConfig; /** * the config of service user @@ -18,7 +18,7 @@ * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ -class UserPoolConfig extends PropertyPoolConfig +class UserPoolConfig extends PoolProperties { /** * the name of pool diff --git a/config/beans/service.php b/config/beans/service.php index 17893e2d..27e3a443 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -10,7 +10,7 @@ 'class' => \Swoft\Rpc\Packer\ServicePacker::class, 'type' => 'json', 'packers' => [ - + 'json' => JsonPacker::class, ], ], ]; \ No newline at end of file diff --git a/test/AbstractTestCase.php b/test/AbstractTestCase.php deleted file mode 100644 index 632ee10c..00000000 --- a/test/AbstractTestCase.php +++ /dev/null @@ -1,17 +0,0 @@ - - * @copyright Copyright 2010-2017 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class AbstractTestCase extends TestCase -{ - -} \ No newline at end of file diff --git a/test/Web/AbstractTestCase.php b/test/Cases/AbstractTestCase.php similarity index 87% rename from test/Web/AbstractTestCase.php rename to test/Cases/AbstractTestCase.php index e4524220..0a397f74 100644 --- a/test/Web/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -1,22 +1,23 @@ * @copyright Copyright 2010-2017 Swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ -abstract class AbstractTestCase extends \Swoft\Test\AbstractTestCase +class AbstractTestCase extends TestCase { - const ACCEPT_VIEW = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"; const ACCEPT_JSON = 'application/json'; const ACCEPT_RAW = 'text/plain'; @@ -36,9 +37,16 @@ public function request($method, $uri, $parameters = [], $accept = self::ACCEPT_ $method = strtoupper($method); $swooleResponse = new TestSwooleResponse(); $swooleRequest = new TestSwooleRequest(); - $swooleRequest->setRawContent($rawContent); + $this->buildMockRequest($method, $uri, $parameters, $accept, $swooleRequest, $headers); - return dispatcher_server()->doDispatcher($swooleRequest, $swooleResponse);; + + $swooleRequest->setRawContent($rawContent); + + $request = Request::loadFromSwooleRequest($swooleRequest); + $response = new Response($swooleResponse); + + + return dispatcher_server()->doDispatcher($request, $response);; } /** @@ -49,7 +57,7 @@ public function request($method, $uri, $parameters = [], $accept = self::ACCEPT_ * @param $swooleRequest * @param array $headers */ - protected function buildMockRequest($method, $uri, $parameters, $accept, $swooleRequest, $headers = []) + protected function buildMockRequest($method, $uri, $parameters, $accept, &$swooleRequest, $headers = []) { $urlAry = parse_url(http://github.com/$uri); $urlParams = []; @@ -95,5 +103,4 @@ protected function buildMockRequest($method, $uri, $parameters, $accept, $swoole $swooleRequest->get = array_merge($urlParams, $get); } } - -} +} \ No newline at end of file diff --git a/test/Web/DemoControllerTest.php b/test/Cases/DemoControllerTest.php similarity index 95% rename from test/Web/DemoControllerTest.php rename to test/Cases/DemoControllerTest.php index 4279d426..bfe1aee6 100644 --- a/test/Web/DemoControllerTest.php +++ b/test/Cases/DemoControllerTest.php @@ -1,8 +1,6 @@ request('GET', '/redis/testFunc2', [], parent::ACCEPT_JSON); $response->assertSuccessful()->assertJson($expected); diff --git a/test/Web/RestTest.php b/test/Cases/RestTest.php similarity index 99% rename from test/Web/RestTest.php rename to test/Cases/RestTest.php index f48535e3..312db5a2 100644 --- a/test/Web/RestTest.php +++ b/test/Cases/RestTest.php @@ -1,6 +1,6 @@ Date: Sat, 27 Jan 2018 15:33:29 +0800 Subject: [PATCH 063/643] rm routes.php --- app/routes.php | 12 --- composer.json | 193 +++++++++++++++++++++++++------------------------ 2 files changed, 97 insertions(+), 108 deletions(-) delete mode 100644 app/routes.php diff --git a/app/routes.php b/app/routes.php deleted file mode 100644 index ffeb1ff6..00000000 --- a/app/routes.php +++ /dev/null @@ -1,12 +0,0 @@ -get('/', IndexController::class); -$router->get('/user/{uid}/book/{bid}/{bool}/{name}', function (bool $bool, Request $request, int $bid, string $name, int $uid, Response $response){ - return ['clouse', $bid, $uid, $bool, $name, get_class($request), get_class($response)]; -}); diff --git a/composer.json b/composer.json index 2e5b9cd1..a367f671 100644 --- a/composer.json +++ b/composer.json @@ -1,105 +1,106 @@ { - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "microservice framework base on swoole", + "license": "apache2.0", + "require": { + "php": ">=7.0", + "swoft/framework": "dev-component", + "swoft/rpc": "dev-master", + "swoft/rpc-server": "dev-master", + "swoft/rpc-client": "dev-master", + "swoft/http-server": "dev-master", + "swoft/task": "dev-master", + "swoft/http-message": "dev-master", + "swoft/view": "dev-master", + "swoft/db": "dev-master", + "swoft/cache": "dev-master", + "swoft/redis": "dev-master", + "swoft/console": "dev-master", + "swoft/swoft-testing": "dev-master" + }, + "autoload": { + "classmap": [], + "psr-4": { + "App\\": "app/" + } + }, + "autoload-dev": { + "psr-4": { + "Swoft\\Test\\": "test/" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], - "description": "microservice framework base on swoole", - "license": "apache2.0", - "require": { - "php": ">=7.0", - "swoft/framework": "dev-component", - "swoft/rpc": "dev-master", - "swoft/rpc-server": "dev-master", - "swoft/rpc-client": "dev-master", - "swoft/http-server": "dev-master", - "swoft/task": "dev-master", - "swoft/http-message": "dev-master", - "swoft/view": "dev-master", - "swoft/db": "dev-master", - "swoft/cache": "dev-master", - "swoft/redis": "dev-master", - "swoft/console": "dev-master" + "test": "./vendor/bin/phpunit -c phpunit.xml" + }, + "require-dev": { + "eaglewu/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^5.7" + }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/swoft-cloud/framework" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc-server" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-rpc-client" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-http-server" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-task" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-http-message" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-view" + }, + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-db" }, - "autoload": { - "classmap": [], - "psr-4": { - "App\\": "app/" - } + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-cache" }, - "autoload-dev": { - "psr-4": { - "Swoft\\Test\\": "test/" - } + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-redis" }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "test": "./vendor/bin/phpunit -c phpunit.xml" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-console" }, - "require-dev": { - "eaglewu/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^5.7" + { + "type": "git", + "url": "https://github.com/swoft-cloud/swoft-testing" }, - "repositories": { - "0": { - "type": "git", - "url": "https://github.com/swoft-cloud/framework" - }, - "1": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc" - }, - "2": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc-server" - }, - "3": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc-client" - }, - "4": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-http-server" - }, - "5": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-task" - }, - "6": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-http-message" - }, - "7": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-view" - }, - "8": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-db" - }, - "9": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-cache" - }, - "10": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-redis" - }, - "11": { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-console" - }, - "12": { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" - }, - "packagist": { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" - } + { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" } + ] } From da3bba2c4121597bfd60d83ed2964e602416304e Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 27 Jan 2018 23:49:41 +0800 Subject: [PATCH 064/643] Update composer.json --- composer.json | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/composer.json b/composer.json index a367f671..b785e2d0 100644 --- a/composer.json +++ b/composer.json @@ -50,54 +50,6 @@ "type": "git", "url": "https://github.com/swoft-cloud/framework" }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc-server" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-rpc-client" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-http-server" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-task" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-http-message" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-view" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-db" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-cache" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-redis" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-console" - }, - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-testing" - }, { "type": "composer", "url": "https://packagist.phpcomposer.com/" From 416ae2cd70e074637e3cdd45c9c95971eecb0ae0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 28 Jan 2018 10:42:32 +0800 Subject: [PATCH 065/643] Update Swoft.php --- app/Swoft.php | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/Swoft.php b/app/Swoft.php index 8092a612..c3a3caa6 100644 --- a/app/Swoft.php +++ b/app/Swoft.php @@ -1,9 +1,5 @@ Date: Sun, 28 Jan 2018 19:08:00 +0800 Subject: [PATCH 066/643] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 514db7d6..ca3ca633 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "swoft" ], "description": "microservice framework base on swoole", - "license": "apache2.0", + "license": "Apache-2.0", "require": { "php": ">=7.0", "swoft/framework": "dev-master" From 9c43ceee4edde526bdd15275b43cea29fd34950a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 28 Jan 2018 19:09:54 +0800 Subject: [PATCH 067/643] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b785e2d0..4cf0b0fa 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "swoft" ], "description": "microservice framework base on swoole", - "license": "apache2.0", + "license": "Apache-2.0", "require": { "php": ">=7.0", "swoft/framework": "dev-component", From a2f36ad00b0bd51d697d836134160ff816ea7e44 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 28 Jan 2018 19:39:36 +0800 Subject: [PATCH 068/643] Update composer.json --- composer.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/composer.json b/composer.json index 4cf0b0fa..830f0c33 100644 --- a/composer.json +++ b/composer.json @@ -46,10 +46,6 @@ "phpunit/phpunit": "^5.7" }, "repositories": [ - { - "type": "git", - "url": "https://github.com/swoft-cloud/framework" - }, { "type": "composer", "url": "https://packagist.phpcomposer.com/" From de233bef97d0addcc91dfcd8345dc15dd51a441d Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 28 Jan 2018 21:45:36 +0800 Subject: [PATCH 069/643] delete swoft-testing --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 830f0c33..5c578f33 100644 --- a/composer.json +++ b/composer.json @@ -21,8 +21,7 @@ "swoft/db": "dev-master", "swoft/cache": "dev-master", "swoft/redis": "dev-master", - "swoft/console": "dev-master", - "swoft/swoft-testing": "dev-master" + "swoft/console": "dev-master" }, "autoload": { "classmap": [], From 19741af66aa829f785b17e9db8973728067d143a Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 28 Jan 2018 22:42:50 +0800 Subject: [PATCH 070/643] AbstractXxxInterface to AbstractXxx --- config/beans/base.php | 4 ++-- config/beans/service.php | 4 ++-- test/Cases/AbstractTestCase.php | 6 +++--- test/Cases/IndexControllerTest.php | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/beans/base.php b/config/beans/base.php index df89643c..862545a4 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -1,8 +1,8 @@ [ - 'class' => \Swoft\Http\Server\DispatcherServer::class, + 'ServerDispatcher' => [ + 'class' => \Swoft\Http\Server\ServerDispatcher::class, 'middlewares' => [ \Swoft\View\Middleware\ViewMiddleware::class ] diff --git a/config/beans/service.php b/config/beans/service.php index 27e3a443..b66bdc27 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -1,7 +1,7 @@ [ - 'class' => \Swoft\Rpc\Server\DispatcherService::class, + 'ServiceDispatcher' => [ + 'class' => \Swoft\Rpc\Server\ServiceDispatcher::class, ], 'serviceRouter' => [ 'class' => \Swoft\Rpc\Server\Router\HandlerMapping::class, diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php index 0a397f74..c53d9430 100644 --- a/test/Cases/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -6,8 +6,8 @@ use Swoft\Helper\ArrayHelper; use Swoft\Testing\SwooleRequest as TestSwooleRequest; use Swoft\Testing\SwooleResponse as TestSwooleResponse; -use Swoft\Testing\Web\Request; -use Swoft\Testing\Web\Response; +use Swoft\Http\Message\Testing\Web\Request; +use Swoft\Http\Message\Testing\Web\Response; /** * @uses AbstractTestCase @@ -30,7 +30,7 @@ class AbstractTestCase extends TestCase * @param array $headers * @param string $rawContent * - * @return bool|\Swoft\Testing\Web\Response + * @return bool|\Swoft\Http\Message\Testing\Web\Response */ public function request($method, $uri, $parameters = [], $accept = self::ACCEPT_JSON, $headers = [], $rawContent = '') { diff --git a/test/Cases/IndexControllerTest.php b/test/Cases/IndexControllerTest.php index 661df317..db6b48e0 100644 --- a/test/Cases/IndexControllerTest.php +++ b/test/Cases/IndexControllerTest.php @@ -2,7 +2,7 @@ namespace Swoft\Test\Cases; -use Swoft\Testing\Web\Response; +use Swoft\Http\Message\Testing\Web\Response; /** * @uses IndexControllerTest From c5f36b136b546172ee1b85f692d22c83610d87a5 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 28 Jan 2018 23:29:26 +0800 Subject: [PATCH 071/643] add devtool --- composer.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5c578f33..6c39d4b4 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "swoft/db": "dev-master", "swoft/cache": "dev-master", "swoft/redis": "dev-master", - "swoft/console": "dev-master" + "swoft/console": "dev-master", + "swoft/devtool": "dev-master" }, "autoload": { "classmap": [], @@ -45,6 +46,10 @@ "phpunit/phpunit": "^5.7" }, "repositories": [ + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-devtool" + }, { "type": "composer", "url": "https://packagist.phpcomposer.com/" From d4e03f1078142cb8c30a611625d34b0b20c8ba8a Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 29 Jan 2018 15:45:54 +0800 Subject: [PATCH 072/643] Create ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..0cce5afa --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,22 @@ +| Q | A +| ---------------- | ----- +| Bug report? | yes/no +| Feature request? | yes/no +| Swoft version | x.y.z +| Swoole version | x.y.z (by `php --ri swoole`) +| PHP version | x.y.z (by `php -v`) + +**Details** + +> Describe what you are trying to achieve and what goes wrong. + +```php +// paste output here +``` + +> Provide minimal script to reproduce the issue + +```php +// paste code +``` + From aae4995b4d1eca929f701c311661f30341dd7998 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 29 Jan 2018 17:49:40 +0800 Subject: [PATCH 073/643] translate to English --- config/define.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/define.php b/config/define.php index 8a87f48b..c2eb74a5 100644 --- a/config/define.php +++ b/config/define.php @@ -4,14 +4,14 @@ // Constants !defined('DS') && define('DS', DIRECTORY_SEPARATOR); -// 系统名称 +// App name !defined('APP_NAME') && define('APP_NAME', 'swoft'); -// 基础根目录 +// Project base path !defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1)); -// cli命名空间 +// Cli namespace !defined('COMMAND_NS') && define('COMMAND_NS', "App\Commands"); -// 注册别名 +// Register alias $aliases = [ '@root' => BASE_PATH, '@app' => '@root/app', From a09837aecaee04548dd4c3b394f54b4e7b8f13d0 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 29 Jan 2018 17:52:17 +0800 Subject: [PATCH 074/643] translate to English --- app/Swoft.php | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/Swoft.php b/app/Swoft.php index c3a3caa6..e7cbedc3 100644 --- a/app/Swoft.php +++ b/app/Swoft.php @@ -1,13 +1,8 @@ - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + * Class Swoft + * Top level namespace user-side App class */ class Swoft extends \Swoft\App { From 968b28220668acc33e894d33fc30164370795fcf Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 29 Jan 2018 17:58:00 +0800 Subject: [PATCH 075/643] add 'public' folder for static resources --- config/server.php | 16 +++++++++------- public/.gitkeep | 0 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 public/.gitkeep diff --git a/config/server.php b/config/server.php index e751da42..40f68c95 100644 --- a/config/server.php +++ b/config/server.php @@ -26,12 +26,14 @@ 'task_queue' => env('CRONTAB_TASK_QUEUE', 2048), ], 'setting' => [ - 'worker_num' => env('WORKER_NUM', 1), - 'max_request' => env('MAX_REQUEST', 10000), - 'daemonize' => env('DAEMONIZE', 0), - 'dispatch_mode' => env('DISPATCH_MODE', 2), - 'log_file' => env('LOG_FILE', '@runtime/logs/swoole.log'), - 'task_worker_num' => env('TASK_WORKER_NUM', 1), - 'upload_tmp_dir' => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'), + 'worker_num' => env('WORKER_NUM', 1), + 'max_request' => env('MAX_REQUEST', 10000), + 'daemonize' => env('DAEMONIZE', 0), + 'dispatch_mode' => env('DISPATCH_MODE', 2), + 'log_file' => env('LOG_FILE', '@runtime/logs/swoole.log'), + 'task_worker_num' => env('TASK_WORKER_NUM', 1), + 'upload_tmp_dir' => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'), + 'document_root' => env('DOCUMENT_ROOT', BASE_PATH . '/public'), + 'enable_static_handler' => env('ENABLE_STATIC_HANDLER', true), ], ]; \ No newline at end of file diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 00000000..e69de29b From 2aee4be44f3804b8a20c77f2e453bed82a1c05d2 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 30 Jan 2018 19:17:32 +0800 Subject: [PATCH 076/643] corebean --- bin/bootstrap.php | 6 +++++- config/beans/base.php | 27 ++++----------------------- config/beans/console.php | 4 ++++ config/beans/log.php | 11 +++++------ config/beans/service.php | 13 ------------- config/define.php | 6 +++--- config/properties/app.php | 3 +++ 7 files changed, 24 insertions(+), 46 deletions(-) create mode 100644 config/beans/console.php diff --git a/bin/bootstrap.php b/bin/bootstrap.php index 0ccdd532..255059d4 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -3,4 +3,8 @@ require_once dirname(__FILE__, 2) . '/config/define.php'; // init the factory of bean -\Swoft\Bean\BeanFactory::init(); \ No newline at end of file +\Swoft\Bean\BeanFactory::init(); + +/* @var \Swoft\Bootstrap\Boots\Bootable $bootstrap*/ +$bootstrap = \Swoft\App::getBean(\Swoft\Bootstrap\Bootstrap::class); +$bootstrap->bootstrap(); \ No newline at end of file diff --git a/config/beans/base.php b/config/beans/base.php index 862545a4..0c16c70d 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -1,51 +1,32 @@ [ - 'class' => \Swoft\Http\Server\ServerDispatcher::class, - 'middlewares' => [ - \Swoft\View\Middleware\ViewMiddleware::class - ] - ], 'application' => [ 'id' => APP_NAME, 'name' => APP_NAME, 'errorAction' => '/error/index', 'useProvider' => false, ], - 'balancerSelector' => [ - 'class' => \Swoft\Pool\BalancerSelector::class, - 'balancers' => [ - - ], - ], - 'providerSelector' => [ - 'class' => \Swoft\Pool\ProviderSelector::class, - 'providers' => [ - ], + 'ServerDispatcher' => [ + 'middlewares' => [ + \Swoft\View\Middleware\ViewMiddleware::class + ] ], 'httpRouter' => [ - 'class' => \Swoft\Http\Server\Router\HandlerMapping::class, 'ignoreLastSep' => false, 'tmpCacheNumber' => 1000, 'matchAll' => '', ], 'requestParser' => [ - 'class' => \Swoft\Http\Server\Parser\RequestParser::class, 'parsers' => [ ], ], 'view' => [ - 'class' => \Swoft\View\Base\View::class, 'viewsPath' => '@resources/views/', ], - 'eventManager' => [ - 'class' => \Swoft\Event\EventManager::class, - ], 'cache' => [ - 'class' => \Swoft\Cache\Cache::class, 'driver' => 'redis', 'drivers' => [ 'redis' => \Swoft\Redis\RedisCache::class diff --git a/config/beans/console.php b/config/beans/console.php new file mode 100644 index 00000000..05e0b10e --- /dev/null +++ b/config/beans/console.php @@ -0,0 +1,4 @@ + [ "class" => \Swoft\Log\FileHandler::class, @@ -17,17 +17,16 @@ 'formatter' => '${lineFormatter}', "levels" => [ \Swoft\Log\Logger::ERROR, - \Swoft\Log\Logger::WARNING - ] + \Swoft\Log\Logger::WARNING, + ], ], "logger" => [ - "class" => \Swoft\Log\Logger::class, "name" => APP_NAME, "flushInterval" => 100, "flushRequest" => true, "handlers" => [ '${noticeHandler}', - '${applicationHandler}' - ] + '${applicationHandler}', + ], ], ]; diff --git a/config/beans/service.php b/config/beans/service.php index b66bdc27..60c0351d 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -1,16 +1,3 @@ [ - 'class' => \Swoft\Rpc\Server\ServiceDispatcher::class, - ], - 'serviceRouter' => [ - 'class' => \Swoft\Rpc\Server\Router\HandlerMapping::class, - ], - 'servicePacker' => [ - 'class' => \Swoft\Rpc\Packer\ServicePacker::class, - 'type' => 'json', - 'packers' => [ - 'json' => JsonPacker::class, - ], - ], ]; \ No newline at end of file diff --git a/config/define.php b/config/define.php index 8a87f48b..c9ea6380 100644 --- a/config/define.php +++ b/config/define.php @@ -8,8 +8,6 @@ !defined('APP_NAME') && define('APP_NAME', 'swoft'); // 基础根目录 !defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1)); -// cli命名空间 -!defined('COMMAND_NS') && define('COMMAND_NS', "App\Commands"); // 注册别名 $aliases = [ @@ -21,6 +19,8 @@ '@resources' => '@root/resources', '@beans' => '@configs/beans', '@properties' => '@configs/properties', - '@commands' => '@app/Commands' + '@commands' => '@app/Commands', + '@console' => '@beans/console.php', ]; + App::setAliases($aliases); diff --git a/config/properties/app.php b/config/properties/app.php index ab5b560c..48575592 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -2,6 +2,9 @@ return [ "version" => '1.0', 'autoInitBean' => true, + 'bootScan' => [ + 'App\Commands' + ], 'beanScan' => [ 'App\Controllers', 'App\Models', From ec0d80877093629537960919149cc5871dbf3012 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 30 Jan 2018 20:28:36 +0800 Subject: [PATCH 077/643] recover unit --- test/Cases/AbstractTestCase.php | 6 ++++-- test/Cases/RouteTest.php | 14 +++++++------- test/bootstrap.php | 2 -- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php index c53d9430..0ec0c142 100644 --- a/test/Cases/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -3,6 +3,7 @@ namespace Swoft\Test\Cases; use PHPUnit\Framework\TestCase; +use Swoft\App; use Swoft\Helper\ArrayHelper; use Swoft\Testing\SwooleRequest as TestSwooleRequest; use Swoft\Testing\SwooleResponse as TestSwooleResponse; @@ -45,8 +46,9 @@ public function request($method, $uri, $parameters = [], $accept = self::ACCEPT_ $request = Request::loadFromSwooleRequest($swooleRequest); $response = new Response($swooleResponse); - - return dispatcher_server()->doDispatcher($request, $response);; + /** @var \Swoft\Http\Server\ServerDispatcher $dispatcher */ + $dispatcher = App::getBean('ServerDispatcher'); + return $dispatcher->dispatch($request, $response);; } /** diff --git a/test/Cases/RouteTest.php b/test/Cases/RouteTest.php index feb06670..b9b07544 100644 --- a/test/Cases/RouteTest.php +++ b/test/Cases/RouteTest.php @@ -23,8 +23,8 @@ public function testFuncArgs() 123, true, "test", - "Swoft\\Testing\\Web\\Request", - "Swoft\\Testing\\Web\\Response", + "Swoft\\Http\\Message\\Testing\\Web\\Request", + "Swoft\\Http\\Message\\Testing\\Web\\Response", ]; $response = $this->request('GET', '/route/user/123/book/456/1/test', [], parent::ACCEPT_JSON); $response->assertExactJson($data); @@ -45,7 +45,7 @@ public function testHasNotArg() public function testHasAnyArgs() { $response = $this->request('GET', '/route/hasAnyArgs/123', [], parent::ACCEPT_JSON); - $response->assertExactJson(["Swoft\\Testing\\Web\\Request", 123]); + $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request", 123]); } /** @@ -66,7 +66,7 @@ public function testOptionnalParameter() public function testHasMoreArgs() { $response = $this->request('GET', '/route/hasMoreArgs', [], parent::ACCEPT_JSON); - $response->assertExactJson(["Swoft\\Testing\\Web\\Request", 0]); + $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request", 0]); } /** @@ -75,7 +75,7 @@ public function testHasMoreArgs() public function testNotAnnotation() { $response = $this->request('GET', '/route/notAnnotation', [], parent::ACCEPT_JSON); - $response->assertExactJson(["Swoft\\Testing\\Web\\Request"]); + $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request"]); } /** @@ -84,7 +84,7 @@ public function testNotAnnotation() public function testOnlyFunc() { $response = $this->request('GET', '/route/onlyFunc', [], parent::ACCEPT_JSON); - $response->assertExactJson(["Swoft\\Testing\\Web\\Request"]); + $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request"]); } /** @@ -93,7 +93,7 @@ public function testOnlyFunc() public function testBehindAction() { $response = $this->request('GET', '/route/behind', [], parent::ACCEPT_JSON); - $response->assertExactJson(["Swoft\\Testing\\Web\\Request"]); + $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request"]); } /** diff --git a/test/bootstrap.php b/test/bootstrap.php index 9c77660e..5bb91482 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -13,7 +13,5 @@ ], ]); -$server = new \Swoft\Http\Server\Http\HttpServer(); - $initApplicationContext = new \Swoft\Core\InitApplicationContext(); $initApplicationContext->init(); \ No newline at end of file From 993aaa7a8673d115758a34edcbce6165496500ec Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Thu, 1 Feb 2018 22:01:59 +0800 Subject: [PATCH 078/643] refactor db --- .env.example | 1 + app/Controllers/OrmController.php | 134 ++++++++++++---------------- app/Controllers/RedisController.php | 6 +- composer.json | 12 +-- config/properties/cache.php | 1 + 5 files changed, 69 insertions(+), 85 deletions(-) diff --git a/.env.example b/.env.example index fb5b52c6..ce6daf48 100644 --- a/.env.example +++ b/.env.example @@ -64,6 +64,7 @@ REDIS_TIMEOUT=200 REDIS_USE_PROVIDER=false REDIS_BALANCER=random REDIS_PROVIDER=consul +REDIS_SERIALIZE=1 # the pool of user service USER_POOL_NAME=user diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index b2057de3..d315fa0c 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -10,42 +10,42 @@ use Swoft\Db\Types; /** - * orm使用demo - * * @Controller() - * @uses OrmController - * @version 2017年09月14日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class OrmController { - /** - * AR save操作 - */ public function arSave() { - // $user = new User(); - // // $user->setId(120); - // $user->setName("stelin"); - // $user->setSex(1); - // $user->setDesc("this my desc"); - // $user->setAge(mt_rand(1, 100)); - // $result = $user->save(); - // - // $user->setDesc("this is defer desc"); - // $dataResult = $user->save(true); - // $deferResult = $dataResult->getResult(); - // - // $this->outputJson([$result, $deferResult]); + $user = new User(); + $user->setName("stelin"); + $user->setSex(1); + $user->setDesc("this my desc"); + $user->setAge(mt_rand(1, 100)); + $deferUser = $user->save(); + + $count = new Count(); + $count->setUid(999); + $count->setFans(mt_rand(1, 1000)); + $count->setFollows(mt_rand(1, 1000)); + $deferCount = $count->save(); + + $userResult = $deferUser->getResult(); + $countResult = $deferCount->getResult(); + + $user = new User(); + $user->setName("stelin2"); + $user->setSex(1); + $user->setDesc("this my desc2"); + $user->setAge(mt_rand(1, 100)); + $directUser = $user->save()->getResult(); $count = new Count(); - $count->setUid(346); + $count->setUid($directUser); $count->setFans(mt_rand(1, 1000)); $count->setFollows(mt_rand(1, 1000)); + $directCount = $count->save()->getResult(); - return [$count->save()]; + return [$userResult, $countResult, $directUser, $directCount]; } /** @@ -61,8 +61,7 @@ public function save() $em = EntityManager::create(); // $result = $em->save($user); - $defer = $em->save($user, true); - $result = $defer->getResult(); + $result = $em->save($user)->getResult(); $em->close(); return [$result]; @@ -78,7 +77,7 @@ public function arDelete() $user->setAge(126); // $result = $user->delete(); - $defer = $user->delete(true); + $defer = $user->delete(); return $defer->getResult(); } @@ -93,7 +92,7 @@ public function delete() $em = EntityManager::create(); // $result = $em->delete($user); - $result = $em->delete($user, true); + $result = $em->delete($user); $em->close(); return [$result->getResult()]; @@ -106,7 +105,7 @@ public function deleteId() { $em = EntityManager::create(); // $result = $em->deleteById(Count::class, 396); - $result = $em->deleteById(Count::class, 406, true); + $result = $em->deleteById(Count::class, 406); $em->close(); return [$result->getResult()]; } @@ -118,7 +117,7 @@ public function deleteIds() { $em = EntityManager::create(); // $result = $em->deleteByIds(Count::class, [409, 410]); - $result = $em->deleteByIds(Count::class, [411, 412], true); + $result = $em->deleteByIds(Count::class, [411, 412]); $em->close(); return [$result->getResult()]; } @@ -129,7 +128,7 @@ public function deleteIds() public function arDeleteId() { // $result = User::deleteById(284); - $result = User::deleteById(287, true); + $result = User::deleteById(287); return $result->getResult(); } @@ -140,7 +139,7 @@ public function arDeleteId() public function arDeleteIds() { // $result = User::deleteByIds([291, 292]); - $result = User::deleteByIds([288, 289], true); + $result = User::deleteByIds([288, 289]); return $result->getResult(); } @@ -161,7 +160,7 @@ public function arUpdate() // $result = $user->update(true); // $result = $result->getResult(); - return [$result]; + return [$result->getResult()]; } /** @@ -173,18 +172,9 @@ public function arFind() $user->setSex(1); $user->setAge(93); $query = $user->find(); - // $result = $query->getResult(); - - /* @var User $userResult */ - // $userResult = $query->getResult(User::class); - $defer = $query->getDefer(); - // $result = $defer->getResult(); - - $result = $defer->getResult(User::class); - $ql = $query->getSql(); - var_dump($result); - return [$ql, $result]; + $result = $query->getResult(User::class); + return [$result]; } /** @@ -199,11 +189,10 @@ public function find() // $result = $query->getResult(); // $result = $query->getResult(User::class); // $result = $query->getDefer()->getResult(); - $result = $query->getDefer()->getResult(User::class); - $sql = $query->getSql(); + $result = $query->getResult(User::class); $em->close(); - return [$result, $sql]; + return [$result]; } /** @@ -211,19 +200,13 @@ public function find() */ public function arFindId() { - $query = User::findById(425); - $result = $query->getResult(); - - /* @var User $userObject */ - $userObject = $query->getResult(User::class); + $result = User::findById(425)->getResult(); $query = User::findById(426); - // $deferResult = $query->getDefer()->getResult(); - - /* @var User $deferResult */ - $deferResult = $query->getDefer()->getResult(User::class); - return [$result, $userObject->getName(), $deferResult->getName()]; + /* @var User $user */ + $user = $query->getResult(User::class); + return [$result, $user->getName()]; } /** @@ -235,11 +218,10 @@ public function findId() $query = $em->findById(User::class, 396); // $result = $query->getResult(); // $result = $query->getResult(User::class); - $result = $query->getDefer()->getResult(); - $sql = $query->getSql(); + $result = $query->getResult(); $em->close(); - return [$result, $sql]; + return [$result]; } /** @@ -249,14 +231,12 @@ public function arFindIds() { $query = User::findByIds([416, 417]); - $sql = $query->getSql(); - // $defer = $query->getDefer(); // $result = $defer->getResult(User::class); $result = $query->getResult(); - return [$result, $sql]; + return [$result]; } /** @@ -269,10 +249,9 @@ public function findIds() $result = $query->getResult(); // $result = $query->getResult(User::class); // $result = $query->getDefer()->getResult(User::class); - $sql = $query->getSql(); $em->close(); - return [$result, $sql]; + return [$result]; } /** @@ -283,11 +262,10 @@ public function arQuery() // $query = User::query()->select('*')->andWhere('sex', 1)->orderBy('id',QueryBuilder::ORDER_BY_DESC)->limit(3); // $query = User::query()->selects(['id', 'sex' => 'sex2'])->andWhere('sex', 1)->orderBy('id',QueryBuilder::ORDER_BY_DESC)->limit(3); $query = User::query()->selects(['id', 'sex' => 'sex2'])->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 429) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2); + ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); // $result = $query->getResult(); - $defer = $query->getDefer(); - $result = $defer->getResult(); - return [$result, $query->getSql()]; + $result = $query->getResult(); + return [$result]; } /** @@ -307,10 +285,10 @@ public function ts() $em = EntityManager::create(); $em->beginTransaction(); - $uid = $em->save($user); + $uid = $em->save($user)->getResult(); $count->setUid($uid); - $result = $em->save($count); + $result = $em->save($count)->getResult(); if ($result === false) { $em->rollback(); } else { @@ -325,10 +303,10 @@ public function query() { $em = EntityManager::create(); $query = $em->createQuery(); - $query->select("*")->from(User::class, 'u')->leftJoin(Count::class, ['u.id=c.uid'], 'c')->whereIn('u.id', [419, 420, 421]) - ->orderBy('u.id', QueryBuilder::ORDER_BY_DESC)->limit(2); + $result = $query->select("*")->from(User::class, 'u')->leftJoin(Count::class, ['u.id=c.uid'], 'c')->whereIn('u.id', [419, 420, 421]) + ->orderBy('u.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); // $result = $query->getResult(); - $result = $query->getDefer()->getResult(); + $result = $result->getResult(); $sql = $query->getSql(); $em->close(); @@ -341,10 +319,10 @@ public function query() public function arCon() { $query1 = User::query()->selects(['id', 'sex' => 'sex2'])->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 419) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->getDefer(); + ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); $query2 = User::query()->select("*")->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 420) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->getDefer(); + ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); $result1 = $query1->getResult(); $result2 = $query2->getResult(); @@ -373,7 +351,7 @@ public function sql() $query->setParameter(2, 434); $query->setParameter(3, 431); - $result = $query->getResult(); + $result = $query->execute(); $sql = $query->getSql(); $em->close(); diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 4dd10ae1..17c86f42 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -36,7 +36,11 @@ public function testCache() $result = $this->cache->set('name', 'stelin'); $name = $this->cache->get('name'); - return [$result, $name]; + $this->redis->incr("count"); + + $this->redis->incrBy("count2", 2); + + return [$result, $name, $this->redis->get('count'), $this->redis->get('count2')]; } public function testRedis() diff --git a/composer.json b/composer.json index 6c39d4b4..cb7e1ea2 100644 --- a/composer.json +++ b/composer.json @@ -22,13 +22,17 @@ "swoft/cache": "dev-master", "swoft/redis": "dev-master", "swoft/console": "dev-master", - "swoft/devtool": "dev-master" + "swoft/devtool": "dev-master", + "swoft/http-client": "dev-master" }, "autoload": { "classmap": [], "psr-4": { "App\\": "app/" - } + }, + "files": [ + "app/Swoft.php" + ] }, "autoload-dev": { "psr-4": { @@ -46,10 +50,6 @@ "phpunit/phpunit": "^5.7" }, "repositories": [ - { - "type": "vcs", - "url": "https://github.com/swoft-cloud/swoft-devtool" - }, { "type": "composer", "url": "https://packagist.phpcomposer.com/" diff --git a/config/properties/cache.php b/config/properties/cache.php index 6813297f..66c81ce1 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -14,5 +14,6 @@ 'useProvider' => false, 'provider' => 'consul', 'db' => 1, + 'serialize' => 0, ], ]; \ No newline at end of file From 094acee2f11f830a7455a029451ad4702897db50 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 2 Feb 2018 16:17:46 +0800 Subject: [PATCH 079/643] fix redis --- app/Controllers/RedisController.php | 3 +-- config/beans/base.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 17c86f42..28dea174 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -5,7 +5,6 @@ use Swoft\Bean\Annotation\Inject; use Swoft\Cache\Cache; -use Swoft\Redis\RedisCache; use Swoft\Http\Server\Bean\Annotation\Controller; @@ -27,7 +26,7 @@ class RedisController /** * @Inject() - * @var RedisCache + * @var \Swoft\Redis\Redis */ private $redis; diff --git a/config/beans/base.php b/config/beans/base.php index 0c16c70d..0653eb23 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -29,7 +29,7 @@ 'cache' => [ 'driver' => 'redis', 'drivers' => [ - 'redis' => \Swoft\Redis\RedisCache::class + 'redis' => \Swoft\Redis\Redis::class ] ] ]; From 05156d93eae51d5d9896ebfb12e23c0744923e73 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Sat, 3 Feb 2018 21:45:53 +0800 Subject: [PATCH 080/643] Update --- composer.json | 1 + config/define.php | 1 + 2 files changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 6c39d4b4..58c094f4 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "swoft/rpc-server": "dev-master", "swoft/rpc-client": "dev-master", "swoft/http-server": "dev-master", + "swoft/http-client": "dev-master", "swoft/task": "dev-master", "swoft/http-message": "dev-master", "swoft/view": "dev-master", diff --git a/config/define.php b/config/define.php index b04600ce..43a4d2b5 100644 --- a/config/define.php +++ b/config/define.php @@ -20,6 +20,7 @@ '@beans' => '@configs/beans', '@properties' => '@configs/properties', '@console' => '@beans/console.php', + '@commands' => '@app/command', ]; App::setAliases($aliases); From 7e2dc0b4ada11ea57d97a8fdabb2ab4ae0866044 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 4 Feb 2018 15:34:31 +0800 Subject: [PATCH 081/643] some file code formatting --- .editorconfig | 21 +++++++++++++++++++++ bin/bootstrap.php | 6 +++--- config/beans/log.php | 26 +++++++++++++------------- config/define.php | 4 +--- config/properties/app.php | 22 +++++++++++----------- config/properties/cache.php | 4 ++-- 6 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..e6d48081 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +root = true + +# 对所有文件生效 +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# 对后缀名为 md 的文件生效 +[*.md] +trim_trailing_whitespace = false + +[*.php] +indent_size = 4 + +[resources/views/*.php] +indent_size = 2 + diff --git a/bin/bootstrap.php b/bin/bootstrap.php index 255059d4..5686b502 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -1,10 +1,10 @@ bootstrap(); \ No newline at end of file +$bootstrap->bootstrap(); diff --git a/config/beans/log.php b/config/beans/log.php index 111f71eb..d871d96f 100644 --- a/config/beans/log.php +++ b/config/beans/log.php @@ -1,30 +1,30 @@ [ - "class" => \Swoft\Log\FileHandler::class, - "logFile" => "@runtime/logs/notice.log", + 'noticeHandler' => [ + 'class' => \Swoft\Log\FileHandler::class, + 'logFile' => '@runtime/logs/notice.log', 'formatter' => '${lineFormatter}', - "levels" => [ + 'levels' => [ \Swoft\Log\Logger::NOTICE, \Swoft\Log\Logger::INFO, \Swoft\Log\Logger::DEBUG, \Swoft\Log\Logger::TRACE, ], ], - "applicationHandler" => [ - "class" => \Swoft\Log\FileHandler::class, - "logFile" => "@runtime/logs/error.log", + 'applicationHandler' => [ + 'class' => \Swoft\Log\FileHandler::class, + 'logFile' => '@runtime/logs/error.log', 'formatter' => '${lineFormatter}', - "levels" => [ + 'levels' => [ \Swoft\Log\Logger::ERROR, \Swoft\Log\Logger::WARNING, ], ], - "logger" => [ - "name" => APP_NAME, - "flushInterval" => 100, - "flushRequest" => true, - "handlers" => [ + 'logger' => [ + 'name' => APP_NAME, + 'flushInterval' => 100, + 'flushRequest' => true, + 'handlers' => [ '${noticeHandler}', '${applicationHandler}', ], diff --git a/config/define.php b/config/define.php index 43a4d2b5..f5f5e2cf 100644 --- a/config/define.php +++ b/config/define.php @@ -1,7 +1,5 @@ '@app/command', ]; -App::setAliases($aliases); +\Swoft\App::setAliases($aliases); diff --git a/config/properties/app.php b/config/properties/app.php index 48575592..7776c7e8 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -1,11 +1,11 @@ '1.0', - 'autoInitBean' => true, + 'version' => '1.0', + 'autoInitBean' => true, 'bootScan' => [ 'App\Commands' ], - 'beanScan' => [ + 'beanScan' => [ 'App\Controllers', 'App\Models', 'App\Middlewares', @@ -16,18 +16,18 @@ 'App\Pool', 'App\Exception', ], - 'I18n' => [ + 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', ], - 'env' => 'Base', + 'env' => 'Base', 'user.stelin.steln' => 'fafafa', - 'Service' => [ + 'Service' => [ 'user' => [ 'timeout' => 3000 ] ], - 'database' => require dirname(__FILE__).DS."db.php", - 'cache' => require dirname(__FILE__).DS."cache.php", - 'service' => require dirname(__FILE__).DS."service.php", - 'breaker' => require dirname(__FILE__).DS."breaker.php", -]; \ No newline at end of file + 'database' => require __DIR__ . DS . 'db.php', + 'cache' => require __DIR__ . DS . 'cache.php', + 'service' => require __DIR__ . DS . 'service.php', + 'breaker' => require __DIR__ . DS . 'breaker.php', +]; diff --git a/config/properties/cache.php b/config/properties/cache.php index 66c81ce1..a4e975cc 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -2,7 +2,7 @@ return [ 'redis' => [ 'name' => 'redis', - "uri" => [ + 'uri' => [ '127.0.0.1:6379', '127.0.0.1:6379', ], @@ -16,4 +16,4 @@ 'db' => 1, 'serialize' => 0, ], -]; \ No newline at end of file +]; From 7c3b53ad442abde847c78b273c15b46462c781e3 Mon Sep 17 00:00:00 2001 From: Zhaohui Huang Date: Sun, 4 Feb 2018 23:27:54 +0800 Subject: [PATCH 082/643] Update Dockerfile swoole enable openssl --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8664a04d..e8179160 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ RUN wget https://github.com/swoole/swoole-src/archive/v2.0.12.tar.gz -O swoole.t && ( \ cd swoole \ && phpize \ - && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine \ + && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine --enable-openssl \ && make -j$(nproc) \ && make install \ ) \ From 19d9a100afa1aec53fffbdf78dd5a129bbb91c5b Mon Sep 17 00:00:00 2001 From: Zhaohui Huang Date: Sun, 4 Feb 2018 23:47:49 +0800 Subject: [PATCH 083/643] Update Dockerfile --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index e8179160..11346ee8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN apt-get update \ vim \ zip \ libz-dev \ + libssl-dev \ && apt-get clean RUN curl -sS https://getcomposer.org/installer | php \ From 12b831a61b978e1729d8d1cc3291cc8459558e9b Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 6 Feb 2018 23:32:32 +0800 Subject: [PATCH 084/643] Dockerfile add ssl support --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8664a04d..6e894c4a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN apt-get update \ vim \ zip \ libz-dev \ + libssl-dev \ && apt-get clean RUN curl -sS https://getcomposer.org/installer | php \ @@ -37,7 +38,7 @@ RUN wget https://github.com/swoole/swoole-src/archive/v2.0.12.tar.gz -O swoole.t && ( \ cd swoole \ && phpize \ - && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine \ + && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine --enable-openssl \ && make -j$(nproc) \ && make install \ ) \ @@ -55,4 +56,4 @@ RUN composer install --no-dev \ EXPOSE 80 -CMD ["php", "/var/www/swoft/bin/swoft", "start"] +CMD ["php", "/var/www/swoft/bin/swoft", "start"] \ No newline at end of file From 565814508a38df242d199f7fb8e5b29494ee2c79 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 6 Feb 2018 23:34:40 +0800 Subject: [PATCH 085/643] Dockerfile remove inotify --- Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6e894c4a..bede99cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,8 +44,6 @@ RUN wget https://github.com/swoole/swoole-src/archive/v2.0.12.tar.gz -O swoole.t ) \ && rm -r swoole \ && docker-php-ext-enable swoole -RUN pecl install inotify \ - && docker-php-ext-enable inotify ADD . /var/www/swoft From 03493b33cabe35d9998f2515e4fff341fff89046 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 6 Feb 2018 23:39:33 +0800 Subject: [PATCH 086/643] Remove useless comment, to prevent affect newbie learning --- app/Commands/TestCommand.php | 4 +-- app/Controllers/DemoController.php | 6 ---- app/Controllers/ErrorController.php | 38 ------------------------ app/Controllers/ExceptionController.php | 7 ----- app/Controllers/IndexController.php | 2 -- app/Controllers/MiddlewareController.php | 6 ---- app/Controllers/OrmController.php | 28 ++++++++--------- app/Controllers/Psr7Controller.php | 16 +++++----- app/Controllers/RedisController.php | 5 ---- app/Controllers/RestController.php | 8 +---- app/Controllers/RouteController.php | 6 ---- app/Controllers/RpcController.php | 5 ---- app/Controllers/TaskController.php | 5 ---- app/Controllers/ValidatorController.php | 7 ----- 14 files changed, 25 insertions(+), 118 deletions(-) delete mode 100644 app/Controllers/ErrorController.php diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php index 991d85bc..edfeb787 100644 --- a/app/Commands/TestCommand.php +++ b/app/Commands/TestCommand.php @@ -70,8 +70,8 @@ public function demo() $opt = input()->getOpt('o'); $name = input()->getArg('arg', 'swoft'); - App::trace("this is command log"); - Log::info("this is comamnd info log"); + App::trace('this is command log'); + Log::info('this is comamnd info log'); /* @var UserLogic $logic */ $logic = App::getBean(UserLogic::class); $data = $logic->getUserInfo(['uid1']); diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index 963900e0..b65bd708 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -17,12 +17,6 @@ /** * 控制器demo * @Controller(prefix="/demo2") - * - * @uses DemoController - * @version 2017年08月22日 - * @author stelin - * @copyright Copyright 2010-2016 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class DemoController { diff --git a/app/Controllers/ErrorController.php b/app/Controllers/ErrorController.php deleted file mode 100644 index 0e83be50..00000000 --- a/app/Controllers/ErrorController.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @copyright Copyright 2010-2016 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class ErrorController -{ - /** - * 错误action - * @RequestMapping() - */ - public function index() - { - $response = App::getResponse(); - $exception = $response->getException(); - - $status = $exception->getCode(); - $message = $exception->getMessage(); - $line = $exception->getLine(); - $file = $exception->getFile(); - - $message .= " " . $file . " " . $line; - return ['message' => $message]; - } -} diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php index 59ba8af2..263bdcbf 100644 --- a/app/Controllers/ExceptionController.php +++ b/app/Controllers/ExceptionController.php @@ -9,14 +9,7 @@ use Swoft\Http\Server\Bean\Annotation\RequestMapping; /** - * the demo of exception - * * @Controller("exception") - * @uses ExceptionController - * @version 2018年01月17日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class ExceptionController { diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 2b1509e4..188fd310 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -12,8 +12,6 @@ /** * Class IndexController * @Controller() - * - * @package App\Controllers */ class IndexController { diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php index ff93f27f..182ec394 100644 --- a/app/Controllers/MiddlewareController.php +++ b/app/Controllers/MiddlewareController.php @@ -20,12 +20,6 @@ * @Middlewares({ * @Middleware(ControlerSubMiddleware::class) * }) - * - * @uses MiddlewareController - * @version 2017年11月29日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class MiddlewareController { diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index d315fa0c..8b8d7afc 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -17,9 +17,9 @@ class OrmController public function arSave() { $user = new User(); - $user->setName("stelin"); + $user->setName('stelin'); $user->setSex(1); - $user->setDesc("this my desc"); + $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); $deferUser = $user->save(); @@ -33,9 +33,9 @@ public function arSave() $countResult = $deferCount->getResult(); $user = new User(); - $user->setName("stelin2"); + $user->setName('stelin2'); $user->setSex(1); - $user->setDesc("this my desc2"); + $user->setDesc('this my desc2'); $user->setAge(mt_rand(1, 100)); $directUser = $user->save()->getResult(); @@ -54,9 +54,9 @@ public function arSave() public function save() { $user = new User(); - $user->setName("stelin"); + $user->setName('stelin'); $user->setSex(1); - $user->setDesc("this my desc"); + $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); $em = EntityManager::create(); @@ -153,7 +153,7 @@ public function arUpdate() /* @var User $user */ $user = $query->getResult(User::class); - $user->setName("upateNameUser2"); + $user->setName('upateNameUser2'); $user->setSex(0); $result = $user->update(); @@ -274,9 +274,9 @@ public function arQuery() public function ts() { $user = new User(); - $user->setName("stelin"); + $user->setName('stelin'); $user->setSex(1); - $user->setDesc("this my desc"); + $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); $count = new Count(); @@ -303,8 +303,8 @@ public function query() { $em = EntityManager::create(); $query = $em->createQuery(); - $result = $query->select("*")->from(User::class, 'u')->leftJoin(Count::class, ['u.id=c.uid'], 'c')->whereIn('u.id', [419, 420, 421]) - ->orderBy('u.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); + $result = $query->select('*')->from(User::class, 'u')->leftJoin(Count::class, ['u.id=c.uid'], 'c')->whereIn('u.id', [419, 420, 421]) + ->orderBy('u.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); // $result = $query->getResult(); $result = $result->getResult(); $sql = $query->getSql(); @@ -321,8 +321,8 @@ public function arCon() $query1 = User::query()->selects(['id', 'sex' => 'sex2'])->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 419) ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); - $query2 = User::query()->select("*")->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 420) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); + $query2 = User::query()->select('*')->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 420) + ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); $result1 = $query1->getResult(); $result2 = $query2->getResult(); @@ -345,7 +345,7 @@ public function sql() // $query->setParameter('uid3', 431); // $query->setParameters($params); - $querySql = "SELECT * FROM user AS u LEFT JOIN count AS c ON u.id=c.uid WHERE u.id IN (?, ?, ?) ORDER BY u.id DESC LIMIT 2"; + $querySql = 'SELECT * FROM user AS u LEFT JOIN count AS c ON u.id=c.uid WHERE u.id IN (?, ?, ?) ORDER BY u.id DESC LIMIT 2'; $query = $em->createQuery($querySql); $query->setParameter(1, 433); $query->setParameter(2, 434); diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index e8a41eb3..121fb4ff 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -20,7 +20,7 @@ class Psr7Controller /** * @RequestMapping() - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -34,7 +34,7 @@ public function get(Request $request) /** * @RequestMapping() * - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -48,7 +48,7 @@ public function post(Request $request) /** * @RequestMapping() * - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -65,7 +65,7 @@ public function input(Request $request) /** * @RequestMapping() - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -77,7 +77,7 @@ public function raw(Request $request) /** * @RequestMapping() - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -90,7 +90,7 @@ public function cookies(Request $request) /** * @RequestMapping() * - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -103,7 +103,7 @@ public function header(Request $request) /** * @RequestMapping() - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ @@ -116,7 +116,7 @@ public function json(Request $request) /** * @RequestMapping() - * @param \Swoft\Web\Request $request + * @param \Swoft\Http\Message\Server\Request $request * * @return array */ diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 28dea174..b7798cb6 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -10,11 +10,6 @@ /** * @Controller(prefix="/redis") - * @uses RedisController - * @version 2017-11-12 - * @author huangzhhui - * @copyright Copyright 2010-2017 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class RedisController { diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php index 7fda0841..3f9a5fc8 100644 --- a/app/Controllers/RestController.php +++ b/app/Controllers/RestController.php @@ -8,15 +8,9 @@ use Swoft\Http\Message\Server\Request; /** - * restful和参数验证测试demo + * RESTful和参数验证测试demo * * @Controller(prefix="/user") - * - * @uses RestController - * @version 2017年11月13日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class RestController { diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 85c0d42a..5ff7f1da 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -11,12 +11,6 @@ * action demo * * @Controller(prefix="/route") - * - * @uses TestCommand - * @version 2017年11月26日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class RouteController { diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index 1e46021f..309ed5bd 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -10,11 +10,6 @@ * rpc controller test * * @Controller(prefix="rpc") - * @uses RpcCommand - * @version 2017年11月27日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class RpcController { diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php index d6bf4064..5310d522 100644 --- a/app/Controllers/TaskController.php +++ b/app/Controllers/TaskController.php @@ -7,11 +7,6 @@ /** * @Controller("task") - * @uses TaskController - * @version 2018年01月13日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class TaskController { diff --git a/app/Controllers/ValidatorController.php b/app/Controllers/ValidatorController.php index 7c2baf09..8c52bb50 100644 --- a/app/Controllers/ValidatorController.php +++ b/app/Controllers/ValidatorController.php @@ -13,14 +13,7 @@ use Swoft\Http\Message\Server\Request; /** - * validator - * * @Controller("validator") - * @uses ValidatorController - * @version 2017年12月02日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class ValidatorController { From 35e33f1a4aa367156f3d4e25f1e8ab905135d8bb Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Tue, 6 Feb 2018 23:40:17 +0800 Subject: [PATCH 087/643] optimize --- test/Cases/AbstractTestCase.php | 57 +++++++++-------- test/Cases/MiddlewareTest.php | 6 +- test/Cases/RestTest.php | 10 +-- test/Cases/RouteTest.php | 8 +-- test/Cases/ValidatorControllerTest.php | 84 +++++++++++++------------- test/bootstrap.php | 4 +- 6 files changed, 87 insertions(+), 82 deletions(-) diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php index 0ec0c142..efc724b2 100644 --- a/test/Cases/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -19,7 +19,7 @@ */ class AbstractTestCase extends TestCase { - const ACCEPT_VIEW = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"; + const ACCEPT_VIEW = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'; const ACCEPT_JSON = 'application/json'; const ACCEPT_RAW = 'text/plain'; @@ -30,11 +30,16 @@ class AbstractTestCase extends TestCase * @param string $accept * @param array $headers * @param string $rawContent - * * @return bool|\Swoft\Http\Message\Testing\Web\Response */ - public function request($method, $uri, $parameters = [], $accept = self::ACCEPT_JSON, $headers = [], $rawContent = '') - { + public function request( + $method, + $uri, + $parameters = [], + $accept = self::ACCEPT_JSON, + $headers = [], + $rawContent = '' + ) { $method = strtoupper($method); $swooleResponse = new TestSwooleResponse(); $swooleRequest = new TestSwooleRequest(); @@ -63,35 +68,35 @@ protected function buildMockRequest($method, $uri, $parameters, $accept, &$swool { $urlAry = parse_url(http://github.com/$uri); $urlParams = []; - if(isset($urlAry['query'])){ + if (isset($urlAry['query'])) { parse_str($urlAry['query'], $urlParams); } $defaultHeaders = [ - 'host' => '127.0.0.1', - "connection" => "keep-alive", - "cache-control" => "max-age=0", - "user-agent" => "PHPUnit", - "upgrade-insecure-requests" => "1", - "accept" => $accept, - "dnt" => "1", - "accept-encoding" => "gzip, deflate, br", - "accept-language" => "zh-CN,zh;q=0.8,en;q=0.6,it-IT;q=0.4,it;q=0.2", + 'host' => '127.0.0.1', + 'connection' => 'keep-alive', + 'cache-control' => 'max-age=0', + 'user-agent' => 'PHPUnit', + 'upgrade-insecure-requests' => '1', + 'accept' => $accept, + 'dnt' => '1', + 'accept-encoding' => 'gzip, deflate, br', + 'accept-language' => 'zh-CN,zh;q=0.8,en;q=0.6,it-IT;q=0.4,it;q=0.2', ]; $swooleRequest->fd = 1; $swooleRequest->header = ArrayHelper::merge($headers, $defaultHeaders); $swooleRequest->server = [ - "request_method" => $method, - "request_uri" => $uri, - "path_info" => "/", - "request_time" => microtime(), - "request_time_float" => microtime(true), - "server_port" => 80, - "remote_port" => 54235, - "remote_addr" => "10.0.2.2", - "master_time" => microtime(), - "server_protocol" => "HTTP/1.1", - "server_software" => "swoole-http-server", + 'request_method' => $method, + 'request_uri' => $uri, + 'path_info' => '/', + 'request_time' => microtime(), + 'request_time_float' => microtime(true), + 'server_port' => 80, + 'remote_port' => 54235, + 'remote_addr' => '10.0.2.2', + 'master_time' => microtime(), + 'server_protocol' => 'HTTP/1.1', + 'server_software' => 'swoole-http-server', ]; if ($method == 'GET') { @@ -100,7 +105,7 @@ protected function buildMockRequest($method, $uri, $parameters, $accept, &$swool $swooleRequest->post = $parameters; } - if (!empty($urlParams)) { + if (! empty($urlParams)) { $get = empty($swooleRequest->get) ? [] : $swooleRequest->get; $swooleRequest->get = array_merge($urlParams, $get); } diff --git a/test/Cases/MiddlewareTest.php b/test/Cases/MiddlewareTest.php index 1329d604..53e41dd6 100644 --- a/test/Cases/MiddlewareTest.php +++ b/test/Cases/MiddlewareTest.php @@ -19,7 +19,7 @@ class MiddlewareTest extends AbstractTestCase public function testControllerAndAction() { $response = $this->request('GET', '/md/caa', [], parent::ACCEPT_JSON); - $response->assertExactJson(["middleware"]); + $response->assertExactJson(['middleware']); $response->assertHeader('Middleware-Group-Test', 'success'); $response->assertHeader('Sub-Middleware-Test', 'Success'); $response->assertHeader('Middleware-Action-Test', 'success'); @@ -31,7 +31,7 @@ public function testControllerAndAction() public function testControllerAndAction2() { $response = $this->request('GET', '/md/caa2', [], parent::ACCEPT_JSON); - $response->assertExactJson(["middleware2"]); + $response->assertExactJson(['middleware2']); $response->assertHeader('Middleware-Group-Test', 'success'); $response->assertHeader('Sub-Middleware-Test', 'Success'); $response->assertHeader('Middleware-Action-Test', 'success'); @@ -43,7 +43,7 @@ public function testControllerAndAction2() public function testControlerMiddleware() { $response = $this->request('GET', '/md/cm', [], parent::ACCEPT_JSON); - $response->assertExactJson(["middleware3"]); + $response->assertExactJson(['middleware3']); $response->assertHeader('ControlerTestMiddleware', 'success'); $response->assertHeader('ControlerSubMiddleware', 'success'); } diff --git a/test/Cases/RestTest.php b/test/Cases/RestTest.php index 312db5a2..dfc524ec 100644 --- a/test/Cases/RestTest.php +++ b/test/Cases/RestTest.php @@ -18,7 +18,7 @@ class RestTest extends AbstractTestCase */ public function testList() { - $data = ["list"]; + $data = ['list']; $response = $this->request('GET', '/user', [], parent::ACCEPT_JSON); $response->assertExactJson($data); } @@ -28,7 +28,7 @@ public function testList() */ public function testCreate() { - $data = ["create","stelin"]; + $data = ['create', 'stelin']; $response = $this->request('POST', '/user', ['name' => 'stelin'], parent::ACCEPT_JSON); $response->assertExactJson($data); @@ -50,7 +50,7 @@ public function testCreate() */ public function testGetUser() { - $data = ["getUser",123]; + $data = ['getUser',123]; $response = $this->request('GET', '/user/123', [], parent::ACCEPT_JSON); $response->assertExactJson($data); } @@ -60,7 +60,7 @@ public function testGetUser() */ public function testGetBookFromUser() { - $data = ["bookFromUser",123,"456"]; + $data = ['bookFromUser',123, '456']; $response = $this->request('GET', '/user/123/book/456', [], parent::ACCEPT_JSON); $response->assertExactJson($data); } @@ -70,7 +70,7 @@ public function testGetBookFromUser() */ public function testDeleteUser() { - $data = ["delete",123]; + $data = ['delete',123]; $response = $this->request('DELETE', '/user/123', [], parent::ACCEPT_JSON); $response->assertExactJson($data); } diff --git a/test/Cases/RouteTest.php b/test/Cases/RouteTest.php index b9b07544..fe70fe5d 100644 --- a/test/Cases/RouteTest.php +++ b/test/Cases/RouteTest.php @@ -22,7 +22,7 @@ public function testFuncArgs() 456, 123, true, - "test", + 'test', "Swoft\\Http\\Message\\Testing\\Web\\Request", "Swoft\\Http\\Message\\Testing\\Web\\Response", ]; @@ -54,10 +54,10 @@ public function testHasAnyArgs() public function testOptionnalParameter() { $response = $this->request('GET', '/route/opntion/arg1', [], parent::ACCEPT_JSON); - $response->assertExactJson(["arg1"]); + $response->assertExactJson(['arg1']); $response = $this->request('GET', '/route/opntion', [], parent::ACCEPT_JSON); - $response->assertExactJson([""]); + $response->assertExactJson(['']); } /** @@ -102,6 +102,6 @@ public function testBehindAction() public function testFuncAnyName() { $response = $this->request('GET', '/route/anyName/stelin', [], parent::ACCEPT_JSON); - $response->assertExactJson(["stelin"]); + $response->assertExactJson(['stelin']); } } \ No newline at end of file diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php index e5930992..c771effb 100644 --- a/test/Cases/ValidatorControllerTest.php +++ b/test/Cases/ValidatorControllerTest.php @@ -19,28 +19,28 @@ class ValidatorControllerTest extends AbstractTestCase public function testString() { $response = $this->request('GET', '/validator/string/stelin', [], parent::ACCEPT_JSON); - $response->assertExactJson(["boy", "girl", "stelin"]); + $response->assertExactJson(['boy', 'girl', 'stelin']); $response = $this->request('POST', '/validator/string/c', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "c is too small (minimum is 3)"]); + $response->assertExactJson(['message' => 'c is too small (minimum is 3)']); $response = $this->request('POST', '/validator/string/stelin', ['name' => 'a'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "a is too small (minimum is 3)"]); + $response->assertExactJson(['message' => 'a is too small (minimum is 3)']); $response = $this->request('POST', '/validator/string/stelin?name=b', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "b is too small (minimum is 3)"]); + $response->assertExactJson(['message' => 'b is too small (minimum is 3)']); $response = $this->request('POST', '/validator/string/stelin66666666', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "stelin66666666 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => 'stelin66666666 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/string/stelin', ['name' => 'stelin66666666'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "stelin66666666 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => 'stelin66666666 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/string/stelin?name=stelin66666666', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "stelin66666666 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => 'stelin66666666 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/string/stelinPath?name=stelinGet', ['name' => 'stelinPost'], parent::ACCEPT_JSON); - $response->assertExactJson(["stelinGet", "stelinPost", "stelinPath"]); + $response->assertExactJson(['stelinGet', 'stelinPost', 'stelinPath']); } /** @@ -52,31 +52,31 @@ public function testNumber() $response->assertExactJson([7, 8, 10]); $response = $this->request('POST', '/validator/number/3', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "3 is too small (minimum is 5)"]); + $response->assertExactJson(['message' => '3 is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/6', ['id' => '-2'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "-2 is not number"]); + $response->assertExactJson(['message' => '-2 is not number']); $response = $this->request('POST', '/validator/number/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "2 is too small (minimum is 5)"]); + $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/6?id=-2', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "-2 is not number"]); + $response->assertExactJson(['message' => '-2 is not number']); $response = $this->request('POST', '/validator/number/6?id=2', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "2 is too small (minimum is 5)"]); + $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/12', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "12 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "12 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9?id=12', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "12 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); - $response->assertExactJson(["9", "9", 9]); + $response->assertExactJson(['9', '9', 9]); } /** @@ -85,41 +85,41 @@ public function testNumber() public function testFloat() { $response = $this->request('GET', '/validator/float/a', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => 'a is not float']); + $response->assertExactJson(['message' => 'a is not float']); $response = $this->request('GET', '/validator/float/5', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => '5 is not float']); + $response->assertExactJson(['message' => '5 is not float']); $response = $this->request('POST', '/validator/float/5.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "5 is too small (minimum is 5.1)"]); + $response->assertExactJson(['message' => '5 is too small (minimum is 5.1)']); $response = $this->request('POST', '/validator/float/6.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "6 is too big (maximum is 5.9)"]); + $response->assertExactJson(['message' => '6 is too big (maximum is 5.9)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => 5], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "5 is not float"]); + $response->assertExactJson(['message' => '5 is not float']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.0'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "5 is too small (minimum is 5.1)"]); + $response->assertExactJson(['message' => '5 is too small (minimum is 5.1)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '6.0'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => '6 is too big (maximum is 5.9)']); + $response->assertExactJson(['message' => '6 is too big (maximum is 5.9)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.2'], parent::ACCEPT_JSON); - $response->assertExactJson([5.6, "5.2", 5.2]); + $response->assertExactJson([5.6, '5.2', 5.2]); $response = $this->request('POST', '/validator/float/5.2?id=5', [5.2], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "5 is not float"]); + $response->assertExactJson(['message' => '5 is not float']); $response = $this->request('POST', '/validator/float/5.2?id=5.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "5 is too small (minimum is 5.1)"]); + $response->assertExactJson(['message' => '5 is too small (minimum is 5.1)']); $response = $this->request('POST', '/validator/float/5.2?id=6.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "6 is too big (maximum is 5.9)"]); + $response->assertExactJson(['message' => '6 is too big (maximum is 5.9)']); $response = $this->request('POST', '/validator/float/5.2?id=5.2', ['id' => '5.2'], parent::ACCEPT_JSON); - $response->assertExactJson(["5.2", "5.2", 5.2]); + $response->assertExactJson(['5.2', '5.2', 5.2]); } /** @@ -131,31 +131,31 @@ public function testInteger() $response->assertExactJson([7, 8, 10]); $response = $this->request('POST', '/validator/integer/3', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "3 is too small (minimum is 5)"]); + $response->assertExactJson(['message' => '3 is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/6', ['id' => 'a'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "a is not integer"]); + $response->assertExactJson(['message' => 'a is not integer']); $response = $this->request('POST', '/validator/integer/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "2 is too small (minimum is 5)"]); + $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/6?id=a', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "a is not integer"]); + $response->assertExactJson(['message' => 'a is not integer']); $response = $this->request('POST', '/validator/integer/6?id=2', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "2 is too small (minimum is 5)"]); + $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/12', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "12 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "12 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9?id=12', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "12 is too big (maximum is 10)"]); + $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); - $response->assertExactJson(["9", "9", 9]); + $response->assertExactJson(['9', '9', 9]); } /** @@ -164,15 +164,15 @@ public function testInteger() public function testEnum() { $response = $this->request('POST', '/validator/enum/4', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "4 is not valid enum!"]); + $response->assertExactJson(['message' => '4 is not valid enum!']); $response = $this->request('POST', '/validator/enum/1?name=4', [], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "4 is not valid enum!"]); + $response->assertExactJson(['message' => '4 is not valid enum!']); $response = $this->request('POST', '/validator/enum/1', ['name' => '4'], parent::ACCEPT_JSON); - $response->assertExactJson(["message" => "4 is not valid enum!"]); + $response->assertExactJson(['message' => '4 is not valid enum!']); $response = $this->request('POST', '/validator/enum/1?name=a', ['name' => '3'], parent::ACCEPT_JSON); - $response->assertExactJson(["a", "3", "1"]); + $response->assertExactJson(['a', '3', '1']); } } \ No newline at end of file diff --git a/test/bootstrap.php b/test/bootstrap.php index 5bb91482..74bd6200 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,6 +1,6 @@ Date: Tue, 6 Feb 2018 23:41:36 +0800 Subject: [PATCH 088/643] remove useless config and format --- config/beans/base.php | 16 +++------------ config/define.php | 8 ++++---- config/properties/app.php | 26 ++++++++++-------------- config/properties/cache.php | 4 ++-- config/properties/db.php | 40 ++++++++++++++++++------------------- 5 files changed, 39 insertions(+), 55 deletions(-) diff --git a/config/beans/base.php b/config/beans/base.php index 0653eb23..f4c0e177 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -1,16 +1,9 @@ [ - 'id' => APP_NAME, - 'name' => APP_NAME, - 'errorAction' => '/error/index', - 'useProvider' => false, - ], - 'ServerDispatcher' => [ 'middlewares' => [ - \Swoft\View\Middleware\ViewMiddleware::class + \Swoft\View\Middleware\ViewMiddleware::class, ] ], 'httpRouter' => [ @@ -23,13 +16,10 @@ ], ], - 'view' => [ + 'view' => [ 'viewsPath' => '@resources/views/', ], - 'cache' => [ + 'cache' => [ 'driver' => 'redis', - 'drivers' => [ - 'redis' => \Swoft\Redis\Redis::class - ] ] ]; diff --git a/config/define.php b/config/define.php index f5f5e2cf..f2cc8509 100644 --- a/config/define.php +++ b/config/define.php @@ -1,11 +1,11 @@ '@configs/beans', '@properties' => '@configs/properties', '@console' => '@beans/console.php', - '@commands' => '@app/command', + '@commands' => '@app/command', ]; \Swoft\App::setAliases($aliases); diff --git a/config/properties/app.php b/config/properties/app.php index 7776c7e8..8a21d061 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -1,11 +1,11 @@ '1.0', + 'version' => '1.0', 'autoInitBean' => true, - 'bootScan' => [ + 'bootScan' => [ 'App\Commands' ], - 'beanScan' => [ + 'beanScan' => [ 'App\Controllers', 'App\Models', 'App\Middlewares', @@ -16,18 +16,12 @@ 'App\Pool', 'App\Exception', ], - 'I18n' => [ + 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', ], - 'env' => 'Base', - 'user.stelin.steln' => 'fafafa', - 'Service' => [ - 'user' => [ - 'timeout' => 3000 - ] - ], - 'database' => require __DIR__ . DS . 'db.php', - 'cache' => require __DIR__ . DS . 'cache.php', - 'service' => require __DIR__ . DS . 'service.php', - 'breaker' => require __DIR__ . DS . 'breaker.php', -]; + 'env' => 'Base', + 'database' => require __DIR__ . DS . 'db.php', + 'cache' => require __DIR__ . DS . 'cache.php', + 'service' => require __DIR__ . DS . 'service.php', + 'breaker' => require __DIR__ . DS . 'breaker.php', +]; \ No newline at end of file diff --git a/config/properties/cache.php b/config/properties/cache.php index a4e975cc..9855ba42 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -2,7 +2,7 @@ return [ 'redis' => [ 'name' => 'redis', - 'uri' => [ + 'uri' => [ '127.0.0.1:6379', '127.0.0.1:6379', ], @@ -16,4 +16,4 @@ 'db' => 1, 'serialize' => 0, ], -]; +]; \ No newline at end of file diff --git a/config/properties/db.php b/config/properties/db.php index 871df9cc..853672e9 100644 --- a/config/properties/db.php +++ b/config/properties/db.php @@ -1,32 +1,32 @@ [ - 'name' => 'master', - "uri" => [ - '127.0.0.1:6379', - '127.0.0.1:6379', + 'name' => 'master', + 'uri' => [ + '127.0.0.1:3306', + '127.0.0.1:3306', ], - "maxIdel" => 8, - "maxActive" => 8, - "maxWait" => 8, - "timeout" => 8, - "balancer" => 'random', - "useProvider" => false, + 'maxIdel' => 8, + 'maxActive' => 8, + 'maxWait' => 8, + 'timeout' => 8, + 'balancer' => 'random', + 'useProvider' => false, 'provider' => 'consul', ], 'slave' => [ - 'name' => 'slave', - "uri" => [ - '127.0.0.1:6379', - '127.0.0.1:6379', + 'name' => 'slave', + 'uri' => [ + '127.0.0.1:3306', + '127.0.0.1:3306', ], - "maxIdel" => 8, - "maxActive" => 8, - "maxWait" => 8, - "timeout" => 8, - "balancer" => 'random', - "useProvider" => false, + 'maxIdel' => 8, + 'maxActive' => 8, + 'maxWait' => 8, + 'timeout' => 8, + 'balancer' => 'random', + 'useProvider' => false, 'provider' => 'consul', ], ]; \ No newline at end of file From 1d2a1aba79ca6a28f4953c36a988ed0c301dad2d Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 7 Feb 2018 01:47:50 +0800 Subject: [PATCH 089/643] add session demo --- app/Controllers/IndexController.php | 19 ++-- app/Controllers/SessionController.php | 66 ++++++++++++ composer.json | 145 ++++++++++++++++---------- config/beans/base.php | 1 + 4 files changed, 167 insertions(+), 64 deletions(-) create mode 100644 app/Controllers/SessionController.php diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 188fd310..9063d335 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -21,7 +21,7 @@ class IndexController * @View(template="index/index") * @return array */ - public function index() + public function index(): array { $name = 'Swoft'; $notes = [ @@ -57,7 +57,7 @@ public function index() /** * show view by view function */ - public function templateView() + public function templateView(): Response { $name = 'Swoft View'; $notes = [ @@ -96,9 +96,9 @@ public function templateView() * @View(template="index/index") * @return \Swoft\Contract\Arrayable|__anonymous@836 */ - public function arrayable() + public function arrayable(): Arrayable { - return (new class implements Arrayable + return new class implements Arrayable { /** * @return array @@ -106,7 +106,7 @@ public function arrayable() public function toArray(): array { return [ - 'name' => 'Swoft', + 'name' => 'Swoft', 'notes' => ['New Generation of PHP Framework', 'Hign Performance, Coroutine and Full Stack'], 'links' => [ [ @@ -133,17 +133,17 @@ public function toArray(): array ]; } - }); + }; } /** * @RequestMapping() * @return Response */ - public function absolutePath() + public function absolutePath(): Response { $data = [ - 'name' => 'Swoft', + 'name' => 'Swoft', 'notes' => ['New Generation of PHP Framework', 'Hign Performance, Coroutine and Full Stack'], 'links' => [ [ @@ -184,6 +184,7 @@ public function raw() /** * @RequestMapping() + * @throws \Swoft\Http\Server\Exception\BadRequestException */ public function exception() { @@ -195,7 +196,7 @@ public function exception() * @param Response $response * @return Response */ - public function redirect(Response $response) + public function redirect(Response $response): Response { return $response->redirect('/'); } diff --git a/app/Controllers/SessionController.php b/app/Controllers/SessionController.php new file mode 100644 index 00000000..64d0ebfc --- /dev/null +++ b/app/Controllers/SessionController.php @@ -0,0 +1,66 @@ +all(); + } + + /** + * @RequestMapping() + * @param \Swoft\Http\Message\Server\Request $request + * @return array + */ + public function set(Request $request): array + { + $key = $request->input('key'); + $value = $request->input('value'); + session()->put([$key => $value]); + return session()->all(); + } + + /** + * @RequestMapping() + * @param \Swoft\Http\Message\Server\Request $request + * @return array + */ + public function remove(Request $request): array + { + $key = $request->input('key'); + session()->remove($key); + return session()->all(); + } + + /** + * @RequestMapping() + */ + public function flush() + { + session()->flush(); + return session()->all(); + } + + /** + * @RequestMapping() + */ + public function regenerateId() + { + return session()->migrate(true); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index f0b1ede6..ab4e3334 100644 --- a/composer.json +++ b/composer.json @@ -1,59 +1,94 @@ { - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "microservice framework base on swoole", - "license": "Apache-2.0", - "require": { - "php": ">=7.0", - "swoft/framework": "dev-component", - "swoft/rpc": "dev-master", - "swoft/rpc-server": "dev-master", - "swoft/rpc-client": "dev-master", - "swoft/http-server": "dev-master", - "swoft/http-client": "dev-master", - "swoft/task": "dev-master", - "swoft/http-message": "dev-master", - "swoft/view": "dev-master", - "swoft/db": "dev-master", - "swoft/cache": "dev-master", - "swoft/redis": "dev-master", - "swoft/console": "dev-master", - "swoft/devtool": "dev-master", - "swoft/http-client": "dev-master" - }, - "autoload": { - "classmap": [], - "psr-4": { - "App\\": "app/" + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", + "license": "Apache-2.0", + "require": { + "php": ">=7.0", + "swoft/framework": "dev-component", + "swoft/rpc": "dev-master", + "swoft/rpc-server": "dev-master", + "swoft/rpc-client": "dev-master", + "swoft/http-server": "dev-master", + "swoft/http-client": "dev-master", + "swoft/task": "dev-master", + "swoft/http-message": "dev-master", + "swoft/view": "dev-master", + "swoft/db": "dev-master", + "swoft/cache": "dev-master", + "swoft/redis": "dev-master", + "swoft/console": "dev-master", + "swoft/devtool": "dev-master", + "swoft/session": "dev-master" + }, + "require-dev": { + "eaglewu/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^5.7" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Swoft.php" + ] }, - "files": [ - "app/Swoft.php" + "autoload-dev": { + "psr-4": { + "Swoft\\Test\\": "test/" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml" + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-session" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-rpc" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-rpc-server" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-http-server" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-console" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-cache" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-redis" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-view" + }, + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-framework" + }, + { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" + } ] - }, - "autoload-dev": { - "psr-4": { - "Swoft\\Test\\": "test/" - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "test": "./vendor/bin/phpunit -c phpunit.xml" - }, - "require-dev": { - "eaglewu/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^5.7" - }, - "repositories": [ - { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" - } - ] } diff --git a/config/beans/base.php b/config/beans/base.php index f4c0e177..67a05b1a 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -4,6 +4,7 @@ 'ServerDispatcher' => [ 'middlewares' => [ \Swoft\View\Middleware\ViewMiddleware::class, + \Swoft\Session\Middleware\SessionMiddleware::class, ] ], 'httpRouter' => [ From 29a8c2059e44873e539864ad17f233d739c835f5 Mon Sep 17 00:00:00 2001 From: Zhaohui Huang Date: Fri, 9 Feb 2018 16:58:52 +0800 Subject: [PATCH 090/643] Update Dockerfile Upgrade swoole to v2.1.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 11346ee8..db7e1013 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && ldconfig \ ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.0.12.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.1.0.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From 06bb73f3bc8c9a606a282a22bea8b9750f094acc Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 9 Feb 2018 22:44:28 +0800 Subject: [PATCH 091/643] rpc service --- app/Controllers/RpcController.php | 49 ++++++++++++++----- app/Lib/DemoInterface.php | 33 +++++++++++++ app/Lib/MdDemoInterface.php | 13 ++++++ app/Services/DemoService.php | 45 ++++++++++++++++++ app/Services/DemoServiceV2.php | 45 ++++++++++++++++++ app/Services/MiddlewareService.php | 19 ++------ app/Services/UserService.php | 75 ------------------------------ 7 files changed, 178 insertions(+), 101 deletions(-) create mode 100644 app/Lib/DemoInterface.php create mode 100644 app/Lib/MdDemoInterface.php create mode 100644 app/Services/DemoService.php create mode 100644 app/Services/DemoServiceV2.php delete mode 100644 app/Services/UserService.php diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index 1e46021f..32341784 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -4,28 +4,52 @@ use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; +use Swoft\Rpc\Client\Bean\Annotation\Reference; use Swoft\Rpc\Client\Service\Service; +use App\Lib\DemoInterface; /** * rpc controller test * * @Controller(prefix="rpc") - * @uses RpcCommand - * @version 2017年11月27日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class RpcController { + + /** + * @Reference("user") + * + * @var DemoInterface + */ + private $demoService; + + /** + * @Reference(name="user", version="1.0.1") + * + * @var DemoInterface + */ + private $demoServiceV2; + + + /** + * @Reference("user") + * @var \App\Lib\MdDemoInterface + */ + private $mdDemoService; + /** * @RequestMapping(route="call") * @return array */ public function call() { - $result = Service::call("user", 'User::getUserInfo', [2, 6, 8]); - return ['call', $result]; + $version = $this->demoService->getUser('11'); + $version2 = $this->demoServiceV2->getUser('11'); + + return [ + 'version' => $version, + 'version2' => $version2, + ]; } /** @@ -33,7 +57,8 @@ public function call() */ public function validate() { - $result = Service::call("user", 'User::getUser', [1,2,'boy', '1.3']); + $result = $this->demoService->getUserByCond(1, 2, 'boy', '4'); + return ['validator', $result]; } @@ -43,9 +68,9 @@ public function validate() */ public function parentMiddleware() { - $result = Service::call("user", 'Md::pm'); + $result = $this->mdDemoService->parentMiddleware(); - return ['validator', $result]; + return ['parentMiddleware', $result]; } /** @@ -53,8 +78,8 @@ public function parentMiddleware() */ public function funcMiddleware() { - $result = Service::call("user", 'Md::fm'); + $result = $this->mdDemoService->funcMiddleware(); - return ['validator', $result]; + return ['funcMiddleware', $result]; } } \ No newline at end of file diff --git a/app/Lib/DemoInterface.php b/app/Lib/DemoInterface.php new file mode 100644 index 00000000..b23eb536 --- /dev/null +++ b/app/Lib/DemoInterface.php @@ -0,0 +1,33 @@ + + * [ + * 'uid' => [], + * 'uid2' => [], + * ...... + * ] + *
+     */
+    public function getUsers(array $ids);
+
+    /**
+     * @param string $id
+     *
+     * @return array
+     */
+    public function getUser(string $id);
+
+    public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc");
+}
\ No newline at end of file
diff --git a/app/Lib/MdDemoInterface.php b/app/Lib/MdDemoInterface.php
new file mode 100644
index 00000000..59a6d69a
--- /dev/null
+++ b/app/Lib/MdDemoInterface.php
@@ -0,0 +1,13 @@
+
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
  */
-class MiddlewareService
+class MiddlewareService implements MdDemoInterface
 {
     /**
-     * @Mapping("pm")
-     *
      * @return array
      */
     public function parentMiddleware()
@@ -35,8 +28,6 @@ public function parentMiddleware()
     }
 
     /**
-     * @Mapping("fm")
-     *
      * @Middleware(class=ServiceMiddleware::class)
      * @return array
      */
diff --git a/app/Services/UserService.php b/app/Services/UserService.php
deleted file mode 100644
index 7ad63264..00000000
--- a/app/Services/UserService.php
+++ /dev/null
@@ -1,75 +0,0 @@
-
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
- */
-class UserService
-{
-    /**
-     * 逻辑层
-     *
-     * @Inject()
-     * @var UserLogic
-     */
-    private $userLogic;
-
-    /**
-     * 用户信息
-     *
-     * @Mapping("getUserInfo")
-     * @param array ...$uids
-     *
-     * @return array
-     */
-    public function getUserInfo(...$uids)
-    {
-        return $this->userLogic->getUserInfo($uids);
-    }
-
-    /**
-     * @Mapping("getUser")
-     * @Enum(name="type", values={1,2,3})
-     * @Number(name="uid", min=1, max=10)
-     * @Strings(name="name", min=2, max=5)
-     * @Floats(name="price", min=1.2, max=1.9)
-     *
-     * @param int    $type
-     * @param int    $uid
-     * @param string $name
-     * @param float  $price
-     * @param string $desc  default value
-     * @return array
-     */
-    public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc")
-    {
-        return [$type, $uid, $name, $price, $desc];
-    }
-
-    /**
-     * 未使用注解,默认方法名
-     *
-     * @return array
-     */
-    public function getUserList()
-    {
-        return ['uid1', 'uid2'];
-    }
-}
\ No newline at end of file

From 84d6a543f349b8509471d8caf40caa0649fbb1b7 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Fri, 9 Feb 2018 23:41:03 +0800
Subject: [PATCH 092/643] composer

---
 composer.json | 39 ++-------------------------------------
 1 file changed, 2 insertions(+), 37 deletions(-)

diff --git a/composer.json b/composer.json
index ab4e3334..97d00482 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,8 @@
         "swoft/redis": "dev-master",
         "swoft/console": "dev-master",
         "swoft/devtool": "dev-master",
-        "swoft/session": "dev-master"
+        "swoft/session": "dev-master",
+        "swoft/i18n": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
@@ -50,42 +51,6 @@
         "test": "./vendor/bin/phpunit -c phpunit.xml"
     },
     "repositories": [
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-session"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-rpc"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-rpc-server"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-http-server"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-console"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-cache"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-redis"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-view"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-framework"
-        },
         {
             "type": "composer",
             "url": "https://packagist.phpcomposer.com/"

From 25646da6e3182577d08d3b32e332ab129a28994f Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Fri, 9 Feb 2018 23:48:19 +0800
Subject: [PATCH 093/643] rm i18n

---
 composer.json | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 97d00482..e35263c6 100644
--- a/composer.json
+++ b/composer.json
@@ -24,8 +24,7 @@
         "swoft/redis": "dev-master",
         "swoft/console": "dev-master",
         "swoft/devtool": "dev-master",
-        "swoft/session": "dev-master",
-        "swoft/i18n": "dev-master"
+        "swoft/session": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",

From 59210754a4c30a0e98683bcc6c124da823e88a13 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sat, 10 Feb 2018 14:29:35 +0800
Subject: [PATCH 094/643] i18n and service

---
 app/Controllers/DemoController.php            |  8 ++++----
 app/Controllers/RpcController.php             | 18 ++++++++++++++---
 app/Models/Logic/UserLogic.php                | 20 +++++++++++++++++++
 composer.json                                 |  3 ++-
 .../{messages => languages}/en/default.php    |  0
 resources/{messages => languages}/en/msg.php  |  0
 .../{messages => languages}/zh/default.php    |  0
 resources/{messages => languages}/zh/msg.php  |  0
 8 files changed, 41 insertions(+), 8 deletions(-)
 rename resources/{messages => languages}/en/default.php (100%)
 rename resources/{messages => languages}/en/msg.php (100%)
 rename resources/{messages => languages}/zh/default.php (100%)
 rename resources/{messages => languages}/zh/msg.php (100%)

diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php
index b65bd708..3a9eaa21 100644
--- a/app/Controllers/DemoController.php
+++ b/app/Controllers/DemoController.php
@@ -139,10 +139,10 @@ public function cor()
      */
     public function i18n()
     {
-        $data[] = App::t("title", [], 'zh');
-        $data[] = App::t("title", [], 'en');
-        $data[] = App::t("msg.body", ["stelin", 999], 'en');
-        $data[] = App::t("msg.body", ["stelin", 666], 'en');
+        $data[] = translate("title", [], 'zh');
+        $data[] = translate("title", [], 'en');
+        $data[] = translate("msg.body", ["stelin", 999], 'en');
+        $data[] = translate("msg.body", ["stelin", 666], 'en');
 
         return $data;
     }
diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php
index 32341784..50602346 100644
--- a/app/Controllers/RpcController.php
+++ b/app/Controllers/RpcController.php
@@ -2,11 +2,11 @@
 
 namespace App\Controllers;
 
+use App\Lib\DemoInterface;
+use Swoft\Bean\Annotation\Inject;
 use Swoft\Http\Server\Bean\Annotation\Controller;
 use Swoft\Http\Server\Bean\Annotation\RequestMapping;
 use Swoft\Rpc\Client\Bean\Annotation\Reference;
-use Swoft\Rpc\Client\Service\Service;
-use App\Lib\DemoInterface;
 
 /**
  * rpc controller test
@@ -30,13 +30,18 @@ class RpcController
      */
     private $demoServiceV2;
 
-
     /**
      * @Reference("user")
      * @var \App\Lib\MdDemoInterface
      */
     private $mdDemoService;
 
+    /**
+     * @Inject()
+     * @var \App\Models\Logic\UserLogic
+     */
+    private $logic;
+
     /**
      * @RequestMapping(route="call")
      * @return array
@@ -52,6 +57,13 @@ public function call()
         ];
     }
 
+    public function beanCall()
+    {
+        return [
+            $this->logic->rpcCall()
+        ];
+    }
+
     /**
      * @RequestMapping("validate")
      */
diff --git a/app/Models/Logic/UserLogic.php b/app/Models/Logic/UserLogic.php
index b326a7a3..f372c2d9 100644
--- a/app/Models/Logic/UserLogic.php
+++ b/app/Models/Logic/UserLogic.php
@@ -3,6 +3,7 @@
 namespace App\Models\Logic;
 
 use Swoft\Bean\Annotation\Bean;
+use Swoft\Rpc\Client\Bean\Annotation\Reference;
 
 /**
  * 用户逻辑层
@@ -17,6 +18,25 @@
  */
 class UserLogic
 {
+    /**
+     * @Reference("user")
+     *
+     * @var \App\Lib\DemoInterface
+     */
+    private $demoService;
+
+    /**
+     * @Reference(name="user", version="1.0.1")
+     *
+     * @var \App\Lib\DemoInterface
+     */
+    private $demoServiceV2;
+
+    public function rpcCall()
+    {
+        return ['bean', $this->demoService->getUser('12'), $this->demoServiceV2->getUser('16')];
+    }
+
     public function getUserInfo(array $uids)
     {
         $user = [
diff --git a/composer.json b/composer.json
index e35263c6..97d00482 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,8 @@
         "swoft/redis": "dev-master",
         "swoft/console": "dev-master",
         "swoft/devtool": "dev-master",
-        "swoft/session": "dev-master"
+        "swoft/session": "dev-master",
+        "swoft/i18n": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
diff --git a/resources/messages/en/default.php b/resources/languages/en/default.php
similarity index 100%
rename from resources/messages/en/default.php
rename to resources/languages/en/default.php
diff --git a/resources/messages/en/msg.php b/resources/languages/en/msg.php
similarity index 100%
rename from resources/messages/en/msg.php
rename to resources/languages/en/msg.php
diff --git a/resources/messages/zh/default.php b/resources/languages/zh/default.php
similarity index 100%
rename from resources/messages/zh/default.php
rename to resources/languages/zh/default.php
diff --git a/resources/messages/zh/msg.php b/resources/languages/zh/msg.php
similarity index 100%
rename from resources/messages/zh/msg.php
rename to resources/languages/zh/msg.php

From c62030fb25cfce735dd7bcae779c5d5c7f374c46 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Mon, 12 Feb 2018 23:33:54 +0800
Subject: [PATCH 095/643] refactor task and add process

---
 app/Boot/MyProcess.php             |  32 ++++++
 app/Commands/TestCommand.php       |  12 ++-
 app/Controllers/DemoController.php |   8 +-
 app/Controllers/TaskController.php |  84 ++++++++++++++-
 app/Listener/TaskFinish.php        |  24 +++++
 app/Process/MyProcess.php          |  36 ++-----
 app/Tasks/SyncTask.php             | 160 ++++++++++++++++++++++++++++
 app/Tasks/TestTask.php             | 162 -----------------------------
 composer.json                      |   7 +-
 config/properties/app.php          |   6 +-
 10 files changed, 332 insertions(+), 199 deletions(-)
 create mode 100644 app/Boot/MyProcess.php
 create mode 100644 app/Listener/TaskFinish.php
 create mode 100644 app/Tasks/SyncTask.php
 delete mode 100644 app/Tasks/TestTask.php

diff --git a/app/Boot/MyProcess.php b/app/Boot/MyProcess.php
new file mode 100644
index 00000000..8711a806
--- /dev/null
+++ b/app/Boot/MyProcess.php
@@ -0,0 +1,32 @@
+getPname();
+        $processName = "$pname myProcess process";
+        $process->name($processName);
+
+        echo "Custom boot process \n";
+
+        $result  = Task::deliverByProcess('sync', 'deliverCo', ['p', 'p2']);
+        var_dump($result);
+
+        ProcessBuilder::create('customProcess')->start();
+    }
+}
\ No newline at end of file
diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php
index edfeb787..6bdd70a5 100644
--- a/app/Commands/TestCommand.php
+++ b/app/Commands/TestCommand.php
@@ -8,12 +8,13 @@
 use Swoft\Console\Bean\Annotation\Mapping;
 use Swoft\Console\Input\Input;
 use Swoft\Console\Output\Output;
+use Swoft\Core\Coroutine;
 use Swoft\Log\Log;
 
 /**
  * the group of test command
  *
- * @Command()
+ * @Command(coroutine=true)
  * @uses      TestCommand
  * @version   2017年11月03日
  * @author    stelin 
@@ -44,7 +45,14 @@ class TestCommand
      */
     public function test(Input $input, Output $output)
     {
-        var_dump('test', $input, $output);
+        App::error('this is eror');
+        App::trace('this is trace');
+        Coroutine::create(function (){
+            App::error('this is eror child');
+            App::trace('this is trace child');
+        });
+
+        var_dump('test', $input, $output, Coroutine::id(),Coroutine::tid());
     }
 
     /**
diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php
index 3a9eaa21..91715478 100644
--- a/app/Controllers/DemoController.php
+++ b/app/Controllers/DemoController.php
@@ -96,10 +96,10 @@ public function index2()
      */
     public function task()
     {
-        $result  = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_COR);
-        $mysql   = Task::deliver('test', 'testMysql', [], Task::TYPE_COR);
-        $http    = Task::deliver('test', 'testHttp', [], Task::TYPE_COR, 20);
-        $rpc     = Task::deliver('test', 'testRpc', [], Task::TYPE_COR, 5);
+        $result  = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_CO);
+        $mysql   = Task::deliver('test', 'testMysql', [], Task::TYPE_CO);
+        $http    = Task::deliver('test', 'testHttp', [], Task::TYPE_CO, 20);
+        $rpc     = Task::deliver('test', 'testRpc', [], Task::TYPE_CO, 5);
         $result1 = Task::deliver('test', 'asyncTask', [], Task::TYPE_ASYNC);
 
         return [$rpc, $http, $mysql, $result, $result1];
diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php
index 5310d522..ba69ef06 100644
--- a/app/Controllers/TaskController.php
+++ b/app/Controllers/TaskController.php
@@ -10,9 +10,87 @@
  */
 class TaskController
 {
-    public function cor()
+    /**
+     * Deliver co task
+     *
+     * @return array
+     */
+    public function co()
     {
-        $result  = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_COR);
-        return [$result, 1];
+        $result  = Task::deliver('sync', 'deliverCo', ['p', 'p2'], Task::TYPE_CO);
+
+        return [$result];
+    }
+
+    /**
+     * Deliver async task
+     *
+     * @return array
+     */
+    public function async()
+    {
+        $result  = Task::deliver('sync', 'deliverAsync', ['p', 'p2'], Task::TYPE_ASYNC);
+
+        return [$result];
+    }
+
+    /**
+     * Cache task
+     *
+     * @return array
+     */
+    public function cache()
+    {
+        $result  = Task::deliver('sync', 'cache', [], Task::TYPE_CO);
+
+        return [$result];
+    }
+
+    /**
+     * Mysql task
+     *
+     * @return array
+     */
+    public function mysql()
+    {
+        $result  = Task::deliver('sync', 'mysql', [], Task::TYPE_CO);
+
+        return [$result];
+    }
+
+    /**
+     * Http task
+     *
+     * @return array
+     */
+    public function http()
+    {
+        $result  = Task::deliver('sync', 'http', [], Task::TYPE_CO);
+
+        return [$result];
+    }
+
+    /**
+     * Rpc task
+     *
+     * @return array
+     */
+    public function rpc()
+    {
+        $result  = Task::deliver('sync', 'rpc', [], Task::TYPE_CO);
+
+        return [$result];
+    }
+
+    /**
+     * Rpc task
+     *
+     * @return array
+     */
+    public function rpc2()
+    {
+        $result  = Task::deliver('sync', 'rpc2', [], Task::TYPE_CO);
+
+        return [$result];
     }
 }
\ No newline at end of file
diff --git a/app/Listener/TaskFinish.php b/app/Listener/TaskFinish.php
new file mode 100644
index 00000000..dc57e6ad
--- /dev/null
+++ b/app/Listener/TaskFinish.php
@@ -0,0 +1,24 @@
+getParams());
+    }
+}
\ No newline at end of file
diff --git a/app/Process/MyProcess.php b/app/Process/MyProcess.php
index e6a7c537..be15acec 100644
--- a/app/Process/MyProcess.php
+++ b/app/Process/MyProcess.php
@@ -3,39 +3,25 @@
 namespace App\Process;
 
 use Swoft\App;
-use Swoole\Process;
-use Swoft\Bootstrap\Process\AbstractProcessInterface;
+use Swoft\Core\Coroutine;
+use Swoft\Process\Bean\Annotation\Process;
+use Swoft\Process\Process as SwoftProcess;
+use Swoft\Process\ProcessInterface;
 
 /**
- * 自定义进程demo
+ * Custom process
  *
- * @uses      MyProcess
- * @version   2017年10月02日
- * @author    stelin 
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
+ * @Process(name="customProcess", coroutine=true)
  */
-class MyProcess extends AbstractProcessInterface
+class MyProcess implements ProcessInterface
 {
-    /**
-     * 实际进程运行逻辑
-     *
-     * @param Process $process 进程对象
-     */
-    public function run(Process $process)
+    public function run(SwoftProcess $process)
     {
-        $pname = $this->server->getPname();
+        $pname = App::$server->getPname();
         $processName = "$pname myProcess process";
         $process->name($processName);
 
-        $i = 1;
-        while (true) {
-
-            $this->task('test', 'testRpc', [], 5);
-            echo "this my process \n";
-            App::trace("my process count=" . $i);
-            sleep(10);
-            $i++;
-        }
+        echo "Custom child process \n";
+        var_dump(Coroutine::id());
     }
 }
\ No newline at end of file
diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php
new file mode 100644
index 00000000..39859e39
--- /dev/null
+++ b/app/Tasks/SyncTask.php
@@ -0,0 +1,160 @@
+set('cacheKey', 'cache');
+
+        return cache('cacheKey');
+    }
+
+    /**
+     * Mysql task
+     *
+     * @return array
+     */
+    public function mysql(){
+        $result = User::findById(425)->getResult();
+
+        $query = User::findById(426);
+
+        /* @var User $user */
+        $user = $query->getResult(User::class);
+        return [$result, $user->getName()];
+    }
+
+    /**
+     * Http task
+     *
+     * @return mixed
+     */
+    public function http()
+    {
+        $client = new Client([
+                'base_uri' => 'http://127.0.0.1/index/post?a=b',
+                'timeout'  => 2,
+            ]);
+
+        $result = $client->post('http://127.0.0.1/index/post?a=b')->getResponse();
+        $result2 = $client->get('http://www.baidu.com/');
+        $data['result'] = $result;
+        $data['result2'] = $result2;
+        return $data;
+    }
+
+    /**
+     * Rpc task
+     *
+     * @return mixed
+     */
+    public function rpc()
+    {
+        return $this->demoService->getUser('6666');
+    }
+
+    /**
+     * Rpc task
+     *
+     * @return mixed
+     */
+    public function rpc2()
+    {
+        return $this->logic->rpcCall();
+    }
+
+    /**
+     * crontab定时任务
+     * 每一秒执行一次
+     *
+     * @Scheduled(cron="* * * * * *")
+     */
+    public function cronTask()
+    {
+        echo time() . "每一秒执行一次  \n";
+        return 'cron';
+    }
+
+    /**
+     * 每分钟第3-5秒执行
+     *
+     * @Scheduled(cron="3-5 * * * * *")
+     */
+    public function cronooTask()
+    {
+        echo time() . "第3-5秒执行\n";
+        return 'cron';
+    }
+}
diff --git a/app/Tasks/TestTask.php b/app/Tasks/TestTask.php
deleted file mode 100644
index 00dbd1dc..00000000
--- a/app/Tasks/TestTask.php
+++ /dev/null
@@ -1,162 +0,0 @@
-
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
- */
-class TestTask
-{
-    /**
-     * 逻辑层
-     *
-     * @Inject()
-     * @var IndexLogic
-     */
-    private $logic;
-
-    /**
-     * 任务中,使用redis自动切换成同步阻塞redis
-     *
-     * @param mixed $p1
-     * @param mixed $p2
-     *
-     * @return string
-     */
-    public function corTask($p1, $p2)
-    {
-        static $status = 1;
-        $status++;
-        echo "this cor task \n";
-        App::trace("this is task log");
-        $name = cache()->get('name');
-        return 'cor' . " $p1" . " $p2 " . $status . " " . $name;
-    }
-
-    /**
-     * 任务中使用mysql自动切换为同步mysql
-     *
-     * @return bool|\Swoft\Db\DataResult
-     */
-    public function testMysql()
-    {
-        $user = new User();
-        $user->setName("stelin");
-        $user->setSex(1);
-        $user->setDesc("this my desc");
-        $user->setAge(mt_rand(1, 100));
-
-        $count = new Count();
-        $count->setFans(mt_rand(1, 1000));
-        $count->setFollows(mt_rand(1, 1000));
-
-        $em = EntityManager::create();
-        $em->beginTransaction();
-        $uid = $em->save($user);
-        $count->setUid(intval($uid));
-
-        $result = $em->save($count);
-        if ($result === false) {
-            $em->rollback();
-        } else {
-            $em->commit();
-        }
-        $em->close();
-        return $result;
-    }
-
-    /**
-     * 任务中使用HTTP,自动切换成同步curl
-     *
-     * @return mixed
-     */
-    public function testHttp()
-    {
-        $requestData = [
-            'name' => 'boy',
-            'desc' => 'php'
-        ];
-
-        $client = new Client([
-                'base_uri' => 'http://127.0.0.1/index/post?a=b',
-                'timeout'  => 2,
-            ]);
-
-        $result = $client->post('http://127.0.0.1/index/post?a=b')->getResponse();
-        $result2 = $client->get('http://www.baidu.com/');
-        $data['result'] = $result;
-        $data['result2'] = $result2;
-        return $data;
-    }
-
-    /**
-     * 任务中使用rpc,自动切换成同步TCP
-     *
-     * @return mixed
-     */
-    public function testRpc()
-    {
-        var_dump('^^^^^^^^^^^', ApplicationContext::getContext());
-        App::trace("this rpc task worker");
-        $result = Service::call("user", 'User::getUserInfo', [2, 6, 8]);
-        return $result;
-    }
-
-
-    /**
-     * 异步task
-     *
-     * @return string
-     */
-    public function asyncTask()
-    {
-        static $status = 1;
-        $status++;
-        echo "this async task \n";
-        $name = cache()->get('name');
-        App::trace("this is task log");
-        return 'async-' . $status . '-' . $name;
-    }
-
-    /**
-     * crontab定时任务
-     * 每一秒执行一次
-     *
-     * @Scheduled(cron="* * * * * *")
-     */
-    public function cronTask()
-    {
-        echo time() . "每一秒执行一次  \n";
-        return 'cron';
-    }
-
-    /**
-     * 每分钟第3-5秒执行
-     *
-     * @Scheduled(cron="3-5 * * * * *")
-     */
-    public function cronooTask()
-    {
-        echo time() . "第3-5秒执行\n";
-        return 'cron';
-    }
-}
diff --git a/composer.json b/composer.json
index 97d00482..fe691401 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,8 @@
         "swoft/console": "dev-master",
         "swoft/devtool": "dev-master",
         "swoft/session": "dev-master",
-        "swoft/i18n": "dev-master"
+        "swoft/i18n": "dev-master",
+        "swoft/process": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
@@ -51,6 +52,10 @@
         "test": "./vendor/bin/phpunit -c phpunit.xml"
     },
     "repositories": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/swoft-cloud/swoft-process"
+        },
         {
             "type": "composer",
             "url": "https://packagist.phpcomposer.com/"
diff --git a/config/properties/app.php b/config/properties/app.php
index 8a21d061..43ba53e3 100644
--- a/config/properties/app.php
+++ b/config/properties/app.php
@@ -3,7 +3,8 @@
     'version'      => '1.0',
     'autoInitBean' => true,
     'bootScan'     => [
-        'App\Commands'
+        'App\Commands',
+        'App\Boot',
     ],
     'beanScan'     => [
         'App\Controllers',
@@ -11,10 +12,11 @@
         'App\Middlewares',
         'App\Tasks',
         'App\Services',
-        'App\Process',
         'App\Breaker',
         'App\Pool',
         'App\Exception',
+        'App\Listener',
+        'App\Process',
     ],
     'I18n'         => [
         'sourceLanguage' => '@root/resources/messages/',

From 9daa3337f60897f5dfb2e713bb42d444e7733d34 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Tue, 13 Feb 2018 15:21:19 +0800
Subject: [PATCH 096/643] add process

---
 app/Boot/MyProcess.php    | 5 +++++
 app/Process/MyProcess.php | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/app/Boot/MyProcess.php b/app/Boot/MyProcess.php
index 8711a806..193c72c0 100644
--- a/app/Boot/MyProcess.php
+++ b/app/Boot/MyProcess.php
@@ -29,4 +29,9 @@ public function run(SwoftProcess $process)
 
         ProcessBuilder::create('customProcess')->start();
     }
+
+    public function check(): bool
+    {
+        return true;
+    }
 }
\ No newline at end of file
diff --git a/app/Process/MyProcess.php b/app/Process/MyProcess.php
index be15acec..4ac41553 100644
--- a/app/Process/MyProcess.php
+++ b/app/Process/MyProcess.php
@@ -24,4 +24,9 @@ public function run(SwoftProcess $process)
         echo "Custom child process \n";
         var_dump(Coroutine::id());
     }
+
+    public function check(): bool
+    {
+        return true;
+    }
 }
\ No newline at end of file

From 3ef881fb8fe190fe85d677a3552ecb7aab3d9c68 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Tue, 20 Feb 2018 21:13:32 +0800
Subject: [PATCH 097/643] add provider.php

---
 .env.example                                | 12 ++++++++
 app/Controllers/IndexController.php         |  2 +-
 app/Middlewares/ActionTestMiddleware.php    |  2 +-
 app/Middlewares/ControlerSubMiddleware.php  |  2 +-
 app/Middlewares/ControlerTestMiddleware.php |  2 +-
 app/Middlewares/GroupTestMiddleware.php     |  2 +-
 app/Middlewares/ServiceMiddleware.php       |  2 +-
 app/Middlewares/ServiceSubMiddleware.php    |  2 +-
 app/Middlewares/SubMiddleware.php           |  2 +-
 app/Middlewares/SubMiddlewares.php          |  2 +-
 config/properties/provider.php              | 31 +++++++++++++++++++++
 11 files changed, 52 insertions(+), 9 deletions(-)
 create mode 100644 config/properties/provider.php

diff --git a/.env.example b/.env.example
index ce6daf48..0859907d 100644
--- a/.env.example
+++ b/.env.example
@@ -81,3 +81,15 @@ USER_POOL_PROVIDER=consul
 USER_BREAKER_FAIL_COUNT = 3
 USER_BREAKER_SUCCESS_COUNT = 6
 USER_BREAKER_DELAY_TIME = 5000
+
+# the provider of consul
+CONSUL_ADDRESS=http://127.0.0.1
+CONSUL_PORT=8500
+CONSUL_REGISTER_ETO=false
+CONSUL_REGISTER_SERVICE_ADDRESS=http://127.0.0.1
+CONSUL_REGISTER_SERVICE_PORT=88
+CONSUL_REGISTER_CHECK_NAME=user
+CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099
+CONSUL_REGISTER_CHECK_INTERVAL=10
+CONSUL_REGISTER_CHECK_TIMEOUT=1
+CONSUL_DISCOVERY_NAME=user
\ No newline at end of file
diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php
index 9063d335..f3e404d0 100644
--- a/app/Controllers/IndexController.php
+++ b/app/Controllers/IndexController.php
@@ -17,7 +17,7 @@ class IndexController
 {
 
     /**
-     * @RequestMapping()
+     * @RequestMapping("/")
      * @View(template="index/index")
      * @return array
      */
diff --git a/app/Middlewares/ActionTestMiddleware.php b/app/Middlewares/ActionTestMiddleware.php
index 5275daba..385cb310 100644
--- a/app/Middlewares/ActionTestMiddleware.php
+++ b/app/Middlewares/ActionTestMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 
 /**
diff --git a/app/Middlewares/ControlerSubMiddleware.php b/app/Middlewares/ControlerSubMiddleware.php
index 0943a322..e2f52a5c 100644
--- a/app/Middlewares/ControlerSubMiddleware.php
+++ b/app/Middlewares/ControlerSubMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 /**
  * the sub middleware of controler
diff --git a/app/Middlewares/ControlerTestMiddleware.php b/app/Middlewares/ControlerTestMiddleware.php
index 36ebbbce..9ed158d2 100644
--- a/app/Middlewares/ControlerTestMiddleware.php
+++ b/app/Middlewares/ControlerTestMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 /**
  * controler middleware test
diff --git a/app/Middlewares/GroupTestMiddleware.php b/app/Middlewares/GroupTestMiddleware.php
index efdbe9c6..21cff7f9 100644
--- a/app/Middlewares/GroupTestMiddleware.php
+++ b/app/Middlewares/GroupTestMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 
 /**
diff --git a/app/Middlewares/ServiceMiddleware.php b/app/Middlewares/ServiceMiddleware.php
index 4e1e9e30..ef748393 100644
--- a/app/Middlewares/ServiceMiddleware.php
+++ b/app/Middlewares/ServiceMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 /**
  * the middleware of service
diff --git a/app/Middlewares/ServiceSubMiddleware.php b/app/Middlewares/ServiceSubMiddleware.php
index 0e003c0f..12c8c347 100644
--- a/app/Middlewares/ServiceSubMiddleware.php
+++ b/app/Middlewares/ServiceSubMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 /**
  * the sub service of middleware
diff --git a/app/Middlewares/SubMiddleware.php b/app/Middlewares/SubMiddleware.php
index cd6e13f1..d940feb4 100644
--- a/app/Middlewares/SubMiddleware.php
+++ b/app/Middlewares/SubMiddleware.php
@@ -6,7 +6,7 @@
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 
 /**
diff --git a/app/Middlewares/SubMiddlewares.php b/app/Middlewares/SubMiddlewares.php
index f689decb..ecd9b42e 100644
--- a/app/Middlewares/SubMiddlewares.php
+++ b/app/Middlewares/SubMiddlewares.php
@@ -7,7 +7,7 @@
 use Psr\Http\Message\ServerRequestInterface;
 use Swoft\Core\RequestHandler;
 use Swoft\Bean\Annotation\Bean;
-use Swoft\Middleware\MiddlewareInterface;
+use Swoft\Http\Message\Middleware\MiddlewareInterface;
 
 
 /**
diff --git a/config/properties/provider.php b/config/properties/provider.php
new file mode 100644
index 00000000..0d6e59e8
--- /dev/null
+++ b/config/properties/provider.php
@@ -0,0 +1,31 @@
+ [
+        'address' => '',
+        'port'    => 8500,
+        'register' => [
+            'id'                => '',
+            'name'              => '',
+            'tags'              => [],
+            'enableTagOverride' => false,
+            'service'           => [
+                'address' => 'http://127.0.0.1/',
+                'port'   => '88',
+            ],
+            'check'             => [
+                'id'       => '',
+                'name'     => '',
+                'tcp'      => 'localhost:22',
+                'interval' => 10,
+                'timeout'  => 1,
+            ],
+        ],
+        'discovery' => [
+            'name' => 'user',
+            'dc' => 'dc',
+            'near' => '',
+            'tag' =>'',
+            'passing' => true
+        ]
+    ],
+];
\ No newline at end of file

From 4c1b28f69a940fd5c8c1c40858b8b4b22f856762 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Wed, 21 Feb 2018 10:36:05 +0800
Subject: [PATCH 098/643] sg and memory

---
 app/Breaker/UserBreaker.php              |  4 ++--
 app/Controllers/MiddlewareController.php |  4 ++--
 app/Pool/Config/UserPoolConfig.php       |  4 ++--
 app/Services/MiddlewareService.php       |  4 ++--
 composer.json                            | 10 ++++++++--
 5 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php
index 06b165ac..f1f9b23f 100644
--- a/app/Breaker/UserBreaker.php
+++ b/app/Breaker/UserBreaker.php
@@ -2,9 +2,9 @@
 
 namespace App\Breaker;
 
-use Swoft\Bean\Annotation\Breaker;
+use Swoft\Sg\Bean\Annotation\Breaker;
 use Swoft\Bean\Annotation\Value;
-use Swoft\Circuit\CircuitBreaker;
+use Swoft\Sg\Circuit\CircuitBreaker;
 
 /**
  * the breaker of user
diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php
index 182ec394..572e335e 100644
--- a/app/Controllers/MiddlewareController.php
+++ b/app/Controllers/MiddlewareController.php
@@ -3,8 +3,8 @@
 namespace App\Controllers;
 
 use Swoft\Http\Server\Bean\Annotation\Controller;
-use Swoft\Bean\Annotation\Middleware;
-use Swoft\Bean\Annotation\Middlewares;
+use Swoft\Http\Message\Bean\Annotation\Middleware;
+use Swoft\Http\Message\Bean\Annotation\Middlewares;
 use Swoft\Http\Server\Bean\Annotation\RequestMapping;
 use App\Middlewares\GroupTestMiddleware;
 use App\Middlewares\ActionTestMiddleware;
diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php
index 644d58be..130e44cc 100644
--- a/app/Pool/Config/UserPoolConfig.php
+++ b/app/Pool/Config/UserPoolConfig.php
@@ -4,9 +4,9 @@
 
 use Swoft\Bean\Annotation\Bean;
 use Swoft\Bean\Annotation\Value;
-use Swoft\Pool\BalancerSelector;
+use Swoft\Sg\BalancerSelector;
 use Swoft\Pool\PoolProperties;
-use Swoft\Pool\ProviderSelector;
+use Swoft\Sg\ProviderSelector;
 
 /**
  * the config of service user
diff --git a/app/Services/MiddlewareService.php b/app/Services/MiddlewareService.php
index b4c8687d..1bf6b5d5 100644
--- a/app/Services/MiddlewareService.php
+++ b/app/Services/MiddlewareService.php
@@ -5,8 +5,8 @@
 use App\Lib\MdDemoInterface;
 use App\Middlewares\ServiceMiddleware;
 use App\Middlewares\ServiceSubMiddleware;
-use Swoft\Bean\Annotation\Middleware;
-use Swoft\Bean\Annotation\Middlewares;
+use Swoft\Http\Message\Bean\Annotation\Middleware;
+use Swoft\Http\Message\Bean\Annotation\Middlewares;
 use Swoft\Rpc\Server\Bean\Annotation\Service;
 
 /**
diff --git a/composer.json b/composer.json
index fe691401..4da02607 100644
--- a/composer.json
+++ b/composer.json
@@ -26,7 +26,9 @@
         "swoft/devtool": "dev-master",
         "swoft/session": "dev-master",
         "swoft/i18n": "dev-master",
-        "swoft/process": "dev-master"
+        "swoft/process": "dev-master",
+        "swoft/memory": "dev-master",
+        "swoft/sg": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
@@ -54,7 +56,11 @@
     "repositories": [
         {
           "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-process"
+          "url": "https://github.com/swoft-cloud/swoft-memory"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/swoft-cloud/swoft-sg"
         },
         {
             "type": "composer",

From 9a5e70c3c7c10c5448f3bdd168e29f314992a75a Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Wed, 21 Feb 2018 12:24:09 +0800
Subject: [PATCH 099/643] modify breaker

---
 app/Breaker/UserBreaker.php | 5 -----
 composer.json               | 8 --------
 2 files changed, 13 deletions(-)

diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php
index f1f9b23f..00830b9e 100644
--- a/app/Breaker/UserBreaker.php
+++ b/app/Breaker/UserBreaker.php
@@ -10,11 +10,6 @@
  * the breaker of user
  *
  * @Breaker("user")
- * @uses      UserBreaker
- * @version   2017年12月14日
- * @author    stelin 
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
  */
 class UserBreaker extends CircuitBreaker
 {
diff --git a/composer.json b/composer.json
index 4da02607..617a882a 100644
--- a/composer.json
+++ b/composer.json
@@ -54,14 +54,6 @@
         "test": "./vendor/bin/phpunit -c phpunit.xml"
     },
     "repositories": [
-        {
-          "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-memory"
-        },
-        {
-          "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-sg"
-        },
         {
             "type": "composer",
             "url": "https://packagist.phpcomposer.com/"

From f1aaa4e524003c973d0030b37816c372fed3e717 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Thu, 22 Feb 2018 09:26:08 +0800
Subject: [PATCH 100/643] rm desc

---
 app/Commands/TestCommand.php | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php
index 6bdd70a5..3e18ea27 100644
--- a/app/Commands/TestCommand.php
+++ b/app/Commands/TestCommand.php
@@ -12,14 +12,9 @@
 use Swoft\Log\Log;
 
 /**
- * the group of test command
+ * Test command
  *
  * @Command(coroutine=true)
- * @uses      TestCommand
- * @version   2017年11月03日
- * @author    stelin 
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
  */
 class TestCommand
 {

From 5ffd8cd21122ec886abf5ef67358e55925fdb140 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Thu, 22 Feb 2018 13:16:17 +0800
Subject: [PATCH 101/643] modify config db

---
 app/Boot/MyProcess.php    | 2 +-
 config/properties/app.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/Boot/MyProcess.php b/app/Boot/MyProcess.php
index 193c72c0..6398c328 100644
--- a/app/Boot/MyProcess.php
+++ b/app/Boot/MyProcess.php
@@ -12,7 +12,7 @@
 /**
  * Custom process
  *
- * @Process(boot=true)
+ * @Process(boot=false)
  */
 class MyProcess implements ProcessInterface
 {
diff --git a/config/properties/app.php b/config/properties/app.php
index 43ba53e3..eee41bdc 100644
--- a/config/properties/app.php
+++ b/config/properties/app.php
@@ -22,7 +22,7 @@
         'sourceLanguage' => '@root/resources/messages/',
     ],
     'env'          => 'Base',
-    'database'     => require __DIR__ . DS . 'db.php',
+    'db'           => require __DIR__ . DS . 'db.php',
     'cache'        => require __DIR__ . DS . 'cache.php',
     'service'      => require __DIR__ . DS . 'service.php',
     'breaker'      => require __DIR__ . DS . 'breaker.php',

From 584b44b142466189580920317b3e2e08eb91df0e Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Fri, 23 Feb 2018 20:41:03 +0800
Subject: [PATCH 102/643] rm redis params

---
 .env.example                | 3 ---
 config/properties/cache.php | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/.env.example b/.env.example
index 0859907d..ac2fd563 100644
--- a/.env.example
+++ b/.env.example
@@ -61,9 +61,6 @@ REDIS_MAX_IDEL=6
 REDIS_MAX_ACTIVE=10
 REDIS_MAX_WAIT=20
 REDIS_TIMEOUT=200
-REDIS_USE_PROVIDER=false
-REDIS_BALANCER=random
-REDIS_PROVIDER=consul
 REDIS_SERIALIZE=1
 
 # the pool of user service
diff --git a/config/properties/cache.php b/config/properties/cache.php
index 9855ba42..352fe8dd 100644
--- a/config/properties/cache.php
+++ b/config/properties/cache.php
@@ -10,9 +10,6 @@
         'maxActive'   => 8,
         'maxWait'     => 8,
         'timeout'     => 8,
-        'balancer'    => 'random',
-        'useProvider' => false,
-        'provider'    => 'consul',
         'db'          => 1,
         'serialize'   => 0,
     ],

From c9806983e2bf2e5625c4f429021aedf38c7c4643 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Fri, 23 Feb 2018 21:47:13 +0800
Subject: [PATCH 103/643] modify name

---
 config/beans/base.php           | 2 +-
 test/Cases/AbstractTestCase.php | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/config/beans/base.php b/config/beans/base.php
index 67a05b1a..1a7fbf47 100644
--- a/config/beans/base.php
+++ b/config/beans/base.php
@@ -1,7 +1,7 @@
  [
+    'serverDispatcher' => [
         'middlewares' => [
             \Swoft\View\Middleware\ViewMiddleware::class,
             \Swoft\Session\Middleware\SessionMiddleware::class,
diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php
index efc724b2..d4ef72d7 100644
--- a/test/Cases/AbstractTestCase.php
+++ b/test/Cases/AbstractTestCase.php
@@ -52,7 +52,7 @@ public function request(
         $response = new Response($swooleResponse);
 
         /** @var \Swoft\Http\Server\ServerDispatcher $dispatcher */
-        $dispatcher = App::getBean('ServerDispatcher');
+        $dispatcher = App::getBean('serverDispatcher');
         return $dispatcher->dispatch($request, $response);;
     }
 

From 567b1b5e4ef11b776c72ced0008b3a06f3863876 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Fri, 23 Feb 2018 22:19:05 +0800
Subject: [PATCH 104/643] add defer rpc

---
 app/Controllers/RpcController.php | 15 +++++++++++++++
 app/Lib/DemoInterface.php         |  6 ++++++
 app/Services/DemoService.php      |  6 ++++++
 app/Services/DemoServiceV2.php    |  5 +++++
 4 files changed, 32 insertions(+)

diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php
index 50602346..f9d484f0 100644
--- a/app/Controllers/RpcController.php
+++ b/app/Controllers/RpcController.php
@@ -57,6 +57,21 @@ public function call()
         ];
     }
 
+    /**
+     * Defer call
+     */
+    public function defer(){
+        $defer1 = $this->demoService->deferGetUser('123');
+        $defer2 = $this->demoServiceV2->deferGetUsers(['2', '3']);
+        $defer3 = $this->demoServiceV2->deferGetUserByCond(1, 2, 'boy', 1.6);
+
+        $result1 = $defer1->getResult();
+        $result2 = $defer2->getResult();
+        $result3 = $defer3->getResult();
+
+        return [$result1, $result2, $result3];
+    }
+
     public function beanCall()
     {
         return [
diff --git a/app/Lib/DemoInterface.php b/app/Lib/DemoInterface.php
index b23eb536..1c7bcaf9 100644
--- a/app/Lib/DemoInterface.php
+++ b/app/Lib/DemoInterface.php
@@ -2,8 +2,14 @@
 
 namespace App\Lib;
 
+use Swoft\Core\ResultInterface;
+
 /**
  * The interface of demo service
+ *
+ * @method ResultInterface deferGetUsers(array $ids)
+ * @method ResultInterface deferGetUser(string $id)
+ * @method ResultInterface deferGetUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc")
  */
 interface DemoInterface
 {
diff --git a/app/Services/DemoService.php b/app/Services/DemoService.php
index 822ea3a9..6cade9dd 100644
--- a/app/Services/DemoService.php
+++ b/app/Services/DemoService.php
@@ -8,9 +8,15 @@
 use Swoft\Bean\Annotation\Number;
 use Swoft\Bean\Annotation\Strings;
 use Swoft\Rpc\Server\Bean\Annotation\Service;
+use Swoft\Core\ResultInterface;
 
 /**
  * Demo servcie
+ *
+ * @method ResultInterface deferGetUsers(array $ids)
+ * @method ResultInterface deferGetUser(string $id)
+ * @method ResultInterface deferGetUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc")
+ *
  * @Service()
  */
 class DemoService implements DemoInterface
diff --git a/app/Services/DemoServiceV2.php b/app/Services/DemoServiceV2.php
index b5e9d2cb..adafa101 100644
--- a/app/Services/DemoServiceV2.php
+++ b/app/Services/DemoServiceV2.php
@@ -8,9 +8,14 @@
 use Swoft\Bean\Annotation\Number;
 use Swoft\Bean\Annotation\Strings;
 use Swoft\Rpc\Server\Bean\Annotation\Service;
+use Swoft\Core\ResultInterface;
 
 /**
  * Demo service
+ *
+ * @method ResultInterface deferGetUsers(array $ids)
+ * @method ResultInterface deferGetUser(string $id)
+ * @method ResultInterface deferGetUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc")
  * @Service(version="1.0.1")
  */
 class DemoServiceV2 implements DemoInterface

From 14c1020d5dda46ae72682b4cb8e9cbd39bf31869 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Sat, 24 Feb 2018 01:25:14 +0800
Subject: [PATCH 105/643] use swoft/service-governance instead of swoft/sg

---
 composer.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/composer.json b/composer.json
index a9e1f1c1..44a722fd 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,7 @@
         "swoft/i18n": "dev-master",
         "swoft/process": "dev-master",
         "swoft/memory": "dev-master",
-        "swoft/sg": "dev-master"
+        "swoft/service-governance": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
@@ -55,8 +55,8 @@
     },
     "repositories": [
         {
-          "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-framework"
+            "type": "vcs",
+            "url": "https://github.com/swoft-cloud/swoft-framework"
         },
         {
             "type": "composer",

From 047d7a7de3cd7cba1cadada3bcdf3cd75edc229d Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Sat, 24 Feb 2018 02:45:39 +0800
Subject: [PATCH 106/643] update MiddlewareController route info, and update
 MiddlewareTest

---
 app/Controllers/MiddlewareController.php    | 17 ++++------
 app/Middlewares/ActionTestMiddleware.php    |  1 +
 app/Middlewares/ControlerSubMiddleware.php  |  6 ++--
 app/Middlewares/ControlerTestMiddleware.php |  6 ++--
 app/Middlewares/GroupTestMiddleware.php     |  1 +
 app/Middlewares/SubMiddleware.php           |  4 ++-
 test/Cases/MiddlewareTest.php               | 37 ++++++++++-----------
 test/Cases/RouteTest.php                    | 17 ++++++----
 8 files changed, 45 insertions(+), 44 deletions(-)

diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php
index 572e335e..3feb4552 100644
--- a/app/Controllers/MiddlewareController.php
+++ b/app/Controllers/MiddlewareController.php
@@ -14,8 +14,7 @@
 
 
 /**
- * @Controller("md")
- *
+ * @Controller("middleware")
  * @Middleware(class=ControlerTestMiddleware::class)
  * @Middlewares({
  *     @Middleware(ControlerSubMiddleware::class)
@@ -24,37 +23,35 @@
 class MiddlewareController
 {
     /**
-     * @RequestMapping(route="caa")
-     *
+     * @RequestMapping()
      * @Middlewares({
      *     @Middleware(GroupTestMiddleware::class),
      *     @Middleware(ActionTestMiddleware::class)
      * })
      * @Middleware(SubMiddleware::class)
      */
-    public function controllerAndAction()
+    public function action1(): array
     {
         return ['middleware'];
     }
 
     /**
-     * @RequestMapping(route="caa2")
-     *
+     * @RequestMapping()
      * @Middleware(SubMiddleware::class)
      * @Middlewares({
      *     @Middleware(GroupTestMiddleware::class),
      *     @Middleware(ActionTestMiddleware::class)
      * })
      */
-    public function controllerAndAction2()
+    public function action2(): array
     {
         return ['middleware2'];
     }
 
     /**
-     * @RequestMapping("cm")
+     * @RequestMapping()
      */
-    public function controlerMiddleware()
+    public function action3(): array
     {
         return ['middleware3'];
     }
diff --git a/app/Middlewares/ActionTestMiddleware.php b/app/Middlewares/ActionTestMiddleware.php
index 385cb310..07c8f50d 100644
--- a/app/Middlewares/ActionTestMiddleware.php
+++ b/app/Middlewares/ActionTestMiddleware.php
@@ -27,6 +27,7 @@ class ActionTestMiddleware implements MiddlewareInterface
      * @param \Psr\Http\Message\ServerRequestInterface $request
      * @param \Psr\Http\Server\RequestHandlerInterface $handler
      * @return \Psr\Http\Message\ResponseInterface
+     * @throws \InvalidArgumentException
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
diff --git a/app/Middlewares/ControlerSubMiddleware.php b/app/Middlewares/ControlerSubMiddleware.php
index e2f52a5c..a1ba52bb 100644
--- a/app/Middlewares/ControlerSubMiddleware.php
+++ b/app/Middlewares/ControlerSubMiddleware.php
@@ -21,14 +21,14 @@
 class ControlerSubMiddleware implements MiddlewareInterface
 {
     /**
-     * @param \Psr\Http\Message\ServerRequestInterface     $request
+     * @param \Psr\Http\Message\ServerRequestInterface $request
      * @param \Psr\Http\Server\RequestHandlerInterface $handler
-     *
      * @return \Psr\Http\Message\ResponseInterface
+     * @throws \InvalidArgumentException
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         $response = $handler->handle($request);
-        return $response->withAddedHeader('ControlerSubMiddleware', 'success');
+        return $response->withAddedHeader('Controler-Sub-Middleware', 'success');
     }
 }
\ No newline at end of file
diff --git a/app/Middlewares/ControlerTestMiddleware.php b/app/Middlewares/ControlerTestMiddleware.php
index 9ed158d2..ab621048 100644
--- a/app/Middlewares/ControlerTestMiddleware.php
+++ b/app/Middlewares/ControlerTestMiddleware.php
@@ -21,14 +21,14 @@
 class ControlerTestMiddleware implements MiddlewareInterface
 {
     /**
-     * @param \Psr\Http\Message\ServerRequestInterface     $request
+     * @param \Psr\Http\Message\ServerRequestInterface $request
      * @param \Psr\Http\Server\RequestHandlerInterface $handler
-     *
      * @return \Psr\Http\Message\ResponseInterface
+     * @throws \InvalidArgumentException
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         $response = $handler->handle($request);
-        return $response->withAddedHeader('ControlerTestMiddleware', 'success');
+        return $response->withAddedHeader('Controler-Test-Middleware', 'success');
     }
 }
\ No newline at end of file
diff --git a/app/Middlewares/GroupTestMiddleware.php b/app/Middlewares/GroupTestMiddleware.php
index 21cff7f9..0304134a 100644
--- a/app/Middlewares/GroupTestMiddleware.php
+++ b/app/Middlewares/GroupTestMiddleware.php
@@ -27,6 +27,7 @@ class GroupTestMiddleware implements MiddlewareInterface
      * @param \Psr\Http\Message\ServerRequestInterface $request
      * @param \Psr\Http\Server\RequestHandlerInterface $handler
      * @return \Psr\Http\Message\ResponseInterface
+     * @throws \InvalidArgumentException
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
diff --git a/app/Middlewares/SubMiddleware.php b/app/Middlewares/SubMiddleware.php
index d940feb4..43bf0d4d 100644
--- a/app/Middlewares/SubMiddleware.php
+++ b/app/Middlewares/SubMiddleware.php
@@ -27,10 +27,12 @@ class SubMiddleware implements MiddlewareInterface
      * @param \Psr\Http\Message\ServerRequestInterface $request
      * @param \Psr\Http\Server\RequestHandlerInterface $handler
      * @return \Psr\Http\Message\ResponseInterface
+     * @throws \InvalidArgumentException
      */
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
     {
         $response = $handler->handle($request);
-        return $response->withAddedHeader('Sub-Middleware-Test', 'Success');
+        $response = $response->withAddedHeader('Sub-Middleware-Test', 'success');
+        return $response;
     }
 }
\ No newline at end of file
diff --git a/test/Cases/MiddlewareTest.php b/test/Cases/MiddlewareTest.php
index 53e41dd6..b5068279 100644
--- a/test/Cases/MiddlewareTest.php
+++ b/test/Cases/MiddlewareTest.php
@@ -3,48 +3,45 @@
 namespace Swoft\Test\Cases;
 
 /**
- * middleware teste
- *
- * @uses      MiddlewareTest
- * @version   2017年11月29日
- * @author    stelin 
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
+ * Middleware test case
  */
 class MiddlewareTest extends AbstractTestCase
 {
     /**
-     * @covers \App\Controllers\MiddlewareController@controllerAndAction
+     * @covers \App\Controllers\MiddlewareController::action1
+     * @test
      */
-    public function testControllerAndAction()
+    public function action1()
     {
-        $response = $this->request('GET', '/md/caa', [], parent::ACCEPT_JSON);
+        $response = $this->request('GET', '/middleware/action1', [], parent::ACCEPT_JSON);
         $response->assertExactJson(['middleware']);
         $response->assertHeader('Middleware-Group-Test', 'success');
-        $response->assertHeader('Sub-Middleware-Test', 'Success');
+        $response->assertHeader('Sub-Middleware-Test', 'success');
         $response->assertHeader('Middleware-Action-Test', 'success');
     }
 
     /**
-     * @covers \App\Controllers\MiddlewareController@controllerAndAction2
+     * @covers \App\Controllers\MiddlewareController::action2
+     * @test
      */
-    public function testControllerAndAction2()
+    public function action2()
     {
-        $response = $this->request('GET', '/md/caa2', [], parent::ACCEPT_JSON);
+        $response = $this->request('GET', '/middleware/action2', [], parent::ACCEPT_JSON);
         $response->assertExactJson(['middleware2']);
         $response->assertHeader('Middleware-Group-Test', 'success');
-        $response->assertHeader('Sub-Middleware-Test', 'Success');
+        $response->assertHeader('Sub-Middleware-Test', 'success');
         $response->assertHeader('Middleware-Action-Test', 'success');
     }
 
     /**
-     * @covers \App\Controllers\MiddlewareController@controlerMiddleware
+     * @covers \App\Controllers\MiddlewareController::action3
+     * @test
      */
-    public function testControlerMiddleware()
+    public function action3()
     {
-        $response = $this->request('GET', '/md/cm', [], parent::ACCEPT_JSON);
+        $response = $this->request('GET', '/middleware/action3', [], parent::ACCEPT_JSON);
         $response->assertExactJson(['middleware3']);
-        $response->assertHeader('ControlerTestMiddleware', 'success');
-        $response->assertHeader('ControlerSubMiddleware', 'success');
+        $response->assertHeader('Controler-Test-Middleware', 'success');
+        $response->assertHeader('Controler-Sub-Middleware', 'success');
     }
 }
\ No newline at end of file
diff --git a/test/Cases/RouteTest.php b/test/Cases/RouteTest.php
index fe70fe5d..5f2a6d92 100644
--- a/test/Cases/RouteTest.php
+++ b/test/Cases/RouteTest.php
@@ -11,6 +11,9 @@
  * @copyright Copyright 2010-2016 swoft software
  * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
  */
+use Swoft\Http\Message\Testing\Web\Request;
+use Swoft\Http\Message\Testing\Web\Response;
+
 class RouteTest extends AbstractTestCase
 {
     /**
@@ -23,8 +26,8 @@ public function testFuncArgs()
             123,
             true,
             'test',
-            "Swoft\\Http\\Message\\Testing\\Web\\Request",
-            "Swoft\\Http\\Message\\Testing\\Web\\Response",
+            Request::class,
+            Response::class,
         ];
         $response = $this->request('GET', '/route/user/123/book/456/1/test', [], parent::ACCEPT_JSON);
         $response->assertExactJson($data);
@@ -45,7 +48,7 @@ public function testHasNotArg()
     public function testHasAnyArgs()
     {
         $response = $this->request('GET', '/route/hasAnyArgs/123', [], parent::ACCEPT_JSON);
-        $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request", 123]);
+        $response->assertExactJson([Request::class, 123]);
     }
 
     /**
@@ -66,7 +69,7 @@ public function testOptionnalParameter()
     public function testHasMoreArgs()
     {
         $response = $this->request('GET', '/route/hasMoreArgs', [], parent::ACCEPT_JSON);
-        $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request", 0]);
+        $response->assertExactJson([Request::class, 0]);
     }
 
     /**
@@ -75,7 +78,7 @@ public function testHasMoreArgs()
     public function testNotAnnotation()
     {
         $response = $this->request('GET', '/route/notAnnotation', [], parent::ACCEPT_JSON);
-        $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request"]);
+        $response->assertExactJson([Request::class]);
     }
 
     /**
@@ -84,7 +87,7 @@ public function testNotAnnotation()
     public function testOnlyFunc()
     {
         $response = $this->request('GET', '/route/onlyFunc', [], parent::ACCEPT_JSON);
-        $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request"]);
+        $response->assertExactJson([Request::class]);
     }
 
     /**
@@ -93,7 +96,7 @@ public function testOnlyFunc()
     public function testBehindAction()
     {
         $response = $this->request('GET', '/route/behind', [], parent::ACCEPT_JSON);
-        $response->assertExactJson(["Swoft\\Http\\Message\\Testing\\Web\\Request"]);
+        $response->assertExactJson([Request::class]);
     }
 
     /**

From 2628a6c92dfa0c13dc4ec9a5552e7f901aec2cfa Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Sat, 24 Feb 2018 02:46:04 +0800
Subject: [PATCH 107/643] modify serverDispatcher to ServerDispatcher

---
 config/beans/base.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/beans/base.php b/config/beans/base.php
index 1a7fbf47..67a05b1a 100644
--- a/config/beans/base.php
+++ b/config/beans/base.php
@@ -1,7 +1,7 @@
  [
+    'ServerDispatcher' => [
         'middlewares' => [
             \Swoft\View\Middleware\ViewMiddleware::class,
             \Swoft\Session\Middleware\SessionMiddleware::class,

From f14f5da58b168704a8d6f153e3da3d560670c4b1 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Sat, 24 Feb 2018 02:46:50 +0800
Subject: [PATCH 108/643] add json(), view(), raw() methods for quickly send a
 mock request

---
 test/Cases/AbstractTestCase.php | 112 +++++++++++++++++++++++++-------
 1 file changed, 89 insertions(+), 23 deletions(-)

diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php
index d4ef72d7..19f04ad0 100644
--- a/test/Cases/AbstractTestCase.php
+++ b/test/Cases/AbstractTestCase.php
@@ -11,11 +11,9 @@
 use Swoft\Http\Message\Testing\Web\Response;
 
 /**
- * @uses      AbstractTestCase
- * @version   2017年11月03日
- * @author    huangzhhui 
- * @copyright Copyright 2010-2017 Swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
+ * Class AbstractTestCase
+ *
+ * @package Swoft\Test\Cases
  */
 class AbstractTestCase extends TestCase
 {
@@ -24,8 +22,10 @@ class AbstractTestCase extends TestCase
     const ACCEPT_RAW = 'text/plain';
 
     /**
-     * @param        $method
-     * @param        $uri
+     * Send a mock request
+     *
+     * @param string $method
+     * @param string $uri
      * @param array  $parameters
      * @param string $accept
      * @param array  $headers
@@ -33,12 +33,12 @@ class AbstractTestCase extends TestCase
      * @return bool|\Swoft\Http\Message\Testing\Web\Response
      */
     public function request(
-        $method,
-        $uri,
-        $parameters = [],
-        $accept = self::ACCEPT_JSON,
-        $headers = [],
-        $rawContent = ''
+        string $method,
+        string $uri,
+        array $parameters = [],
+        string $accept = self::ACCEPT_JSON,
+        array $headers = [],
+        string $rawContent = ''
     ) {
         $method = strtoupper($method);
         $swooleResponse = new TestSwooleResponse();
@@ -52,20 +52,86 @@ public function request(
         $response = new Response($swooleResponse);
 
         /** @var \Swoft\Http\Server\ServerDispatcher $dispatcher */
-        $dispatcher = App::getBean('serverDispatcher');
-        return $dispatcher->dispatch($request, $response);;
+        $dispatcher = App::getBean('ServerDispatcher');
+        return $dispatcher->dispatch($request, $response);
     }
 
     /**
-     * @param       $method
-     * @param       $uri
-     * @param       $parameters
-     * @param       $accept
-     * @param       $swooleRequest
-     * @param array $headers
+     * Send a mock json request
+     *
+     * @param string $method
+     * @param string $uri
+     * @param array  $parameters
+     * @param array  $headers
+     * @param string $rawContent
+     * @return bool|\Swoft\Http\Message\Testing\Web\Response
+     */
+    public function json(
+        string $method,
+        string $uri,
+        array $parameters = [],
+        array $headers = [],
+        string $rawContent = ''
+    ) {
+        return $this->request($method, $uri, $parameters, self::ACCEPT_JSON, $headers, $rawContent);
+    }
+
+    /**
+     * Send a mock view request
+     *
+     * @param string $method
+     * @param string $uri
+     * @param array  $parameters
+     * @param array  $headers
+     * @param string $rawContent
+     * @return bool|\Swoft\Http\Message\Testing\Web\Response
+     */
+    public function view(
+        string $method,
+        string $uri,
+        array $parameters = [],
+        array $headers = [],
+        string $rawContent = ''
+    ) {
+        return $this->request($method, $uri, $parameters, self::ACCEPT_VIEW, $headers, $rawContent);
+    }
+
+    /**
+     * Send a mock raw content request
+     *
+     * @param string $method
+     * @param string $uri
+     * @param array  $parameters
+     * @param array  $headers
+     * @param string $rawContent
+     * @return bool|\Swoft\Http\Message\Testing\Web\Response
      */
-    protected function buildMockRequest($method, $uri, $parameters, $accept, &$swooleRequest, $headers = [])
-    {
+    public function raw(
+        string $method,
+        string $uri,
+        array $parameters = [],
+        array $headers = [],
+        string $rawContent = ''
+    ) {
+        return $this->request($method, $uri, $parameters, self::ACCEPT_RAW, $headers, $rawContent);
+    }
+
+    /**
+     * @param string               $method
+     * @param string               $uri
+     * @param array                $parameters
+     * @param string               $accept
+     * @param \Swoole\Http\Request $swooleRequest
+     * @param array                $headers
+     */
+    protected function buildMockRequest(
+        string $method,
+        string $uri,
+        array $parameters,
+        string $accept,
+        &$swooleRequest,
+        array $headers = []
+    ) {
         $urlAry = parse_url(http://github.com/$uri);
         $urlParams = [];
         if (isset($urlAry['query'])) {

From 6b6bcc41bf253091d47e87ec28577935c60ab613 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sat, 24 Feb 2018 13:20:59 +0800
Subject: [PATCH 109/643] add repo

---
 composer.json | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/composer.json b/composer.json
index a9e1f1c1..260cd4f9 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,7 @@
         "swoft/i18n": "dev-master",
         "swoft/process": "dev-master",
         "swoft/memory": "dev-master",
-        "swoft/sg": "dev-master"
+        "swoft/service-governance": "dev-master"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
@@ -58,6 +58,10 @@
           "type": "vcs",
           "url": "https://github.com/swoft-cloud/swoft-framework"
         },
+        {
+          "type": "vcs",
+          "url": "https://github.com/swoft-cloud/swoft-service-governance"
+        },
         {
             "type": "composer",
             "url": "https://packagist.phpcomposer.com/"

From c7f9df19619a2b35f77f71aa8efe634ae1910d61 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sat, 24 Feb 2018 14:26:21 +0800
Subject: [PATCH 110/643] fix bug

---
 composer.json                   | 4 ++++
 config/beans/base.php           | 2 +-
 config/properties/db.php        | 8 ++++----
 test/Cases/AbstractTestCase.php | 2 +-
 4 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/composer.json b/composer.json
index 58c62189..768b11ac 100644
--- a/composer.json
+++ b/composer.json
@@ -62,6 +62,10 @@
           "type": "vcs",
           "url": "https://github.com/swoft-cloud/swoft-service-governance"
         },
+        {
+          "type": "vcs",
+          "url": "https://github.com/swoft-cloud/swoft-http-message"
+        },
         {
             "type": "composer",
             "url": "https://packagist.phpcomposer.com/"
diff --git a/config/beans/base.php b/config/beans/base.php
index 67a05b1a..1a7fbf47 100644
--- a/config/beans/base.php
+++ b/config/beans/base.php
@@ -1,7 +1,7 @@
  [
+    'serverDispatcher' => [
         'middlewares' => [
             \Swoft\View\Middleware\ViewMiddleware::class,
             \Swoft\Session\Middleware\SessionMiddleware::class,
diff --git a/config/properties/db.php b/config/properties/db.php
index 853672e9..9754c909 100644
--- a/config/properties/db.php
+++ b/config/properties/db.php
@@ -3,8 +3,8 @@
     'master' => [
         'name'        => 'master',
         'uri'         => [
-            '127.0.0.1:3306',
-            '127.0.0.1:3306',
+            '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
+            '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
         ],
         'maxIdel'     => 8,
         'maxActive'   => 8,
@@ -18,8 +18,8 @@
     'slave' => [
         'name'        => 'slave',
         'uri'         => [
-            '127.0.0.1:3306',
-            '127.0.0.1:3306',
+            '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
+            '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
         ],
         'maxIdel'     => 8,
         'maxActive'   => 8,
diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php
index 19f04ad0..a25a1437 100644
--- a/test/Cases/AbstractTestCase.php
+++ b/test/Cases/AbstractTestCase.php
@@ -52,7 +52,7 @@ public function request(
         $response = new Response($swooleResponse);
 
         /** @var \Swoft\Http\Server\ServerDispatcher $dispatcher */
-        $dispatcher = App::getBean('ServerDispatcher');
+        $dispatcher = App::getBean('serverDispatcher');
         return $dispatcher->dispatch($request, $response);
     }
 

From a9de7fda21081e5c4ea58adb1fa8ca393153a5a3 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Sat, 24 Feb 2018 15:40:15 +0800
Subject: [PATCH 111/643] use lowercase bean name

---
 config/beans/base.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config/beans/base.php b/config/beans/base.php
index 67a05b1a..1a7fbf47 100644
--- a/config/beans/base.php
+++ b/config/beans/base.php
@@ -1,7 +1,7 @@
  [
+    'serverDispatcher' => [
         'middlewares' => [
             \Swoft\View\Middleware\ViewMiddleware::class,
             \Swoft\Session\Middleware\SessionMiddleware::class,

From 3c4a915bb737d01b024489428bf143e17f8ae041 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Sun, 25 Feb 2018 12:19:21 +0800
Subject: [PATCH 112/643] use single quotes

---
 app/Controllers/DemoController.php | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php
index 91715478..aa86adc0 100644
--- a/app/Controllers/DemoController.php
+++ b/app/Controllers/DemoController.php
@@ -82,9 +82,9 @@ public function index(Request $request)
     public function index2()
     {
         Coroutine::create(function () {
-            App::trace("this is child trace" . Coroutine::id());
+            App::trace('this is child trace' . Coroutine::id());
             Coroutine::create(function () {
-                App::trace("this is child child trace" . Coroutine::id());
+                App::trace('this is child child trace' . Coroutine::id());
             });
         });
 
@@ -121,8 +121,8 @@ public function cor()
     {
         // 创建子协程
         Coroutine::create(function () {
-            App::error("child cor error msg");
-            App::trace("child cor error msg");
+            App::error('child cor error msg');
+            App::trace('child cor error msg');
         });
 
         // 当前协程id
@@ -139,10 +139,10 @@ public function cor()
      */
     public function i18n()
     {
-        $data[] = translate("title", [], 'zh');
-        $data[] = translate("title", [], 'en');
-        $data[] = translate("msg.body", ["stelin", 999], 'en');
-        $data[] = translate("msg.body", ["stelin", 666], 'en');
+        $data[] = translate('title', [], 'zh');
+        $data[] = translate('title', [], 'en');
+        $data[] = translate('msg.body', ['stelin', 999], 'en');
+        $data[] = translate('msg.body', ['stelin', 666], 'en');
 
         return $data;
     }

From 54281429d237aaa887e065b88881779e87dcb4e9 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sun, 25 Feb 2018 16:44:07 +0800
Subject: [PATCH 113/643] rm config from db

---
 .env.example                        |  6 ------
 app/Controllers/HttpController.php  | 24 ++++++++++++++++++++++++
 app/Controllers/RedisController.php |  2 +-
 config/properties/db.php            |  6 ------
 4 files changed, 25 insertions(+), 13 deletions(-)
 create mode 100644 app/Controllers/HttpController.php

diff --git a/.env.example b/.env.example
index ac2fd563..3d534999 100644
--- a/.env.example
+++ b/.env.example
@@ -38,9 +38,6 @@ DB_MAX_IDEL=6
 DB_MAX_ACTIVE=10
 DB_MAX_WAIT=20
 DB_TIMEOUT=200
-DB_USE_PROVIDER=false
-DB_BALANCER=random
-DB_PROVIDER=consul
 
 # the pool of slave nodes pool
 DB_SLAVE_NAME=dbSlave
@@ -49,9 +46,6 @@ DB_SLAVE_MAX_IDEL=6
 DB_SLAVE_MAX_ACTIVE=10
 DB_SLAVE_MAX_WAIT=20
 DB_SLAVE_TIMEOUT=200
-DB_SLAVE_USE_PROVIDER=false
-DB_SLAVE_BALANCER=random
-DB_SLAVE_PROVIDER=consul
 
 # the pool of redis
 REDIS_NAME=redis
diff --git a/app/Controllers/HttpController.php b/app/Controllers/HttpController.php
new file mode 100644
index 00000000..c1bc0061
--- /dev/null
+++ b/app/Controllers/HttpController.php
@@ -0,0 +1,24 @@
+get('http://www.swoft.org/')->getResponse()->getBody()->getContents();
+        $response2 = $client->get('http://127.0.0.1/redis/testCache')->getResponse()->getBody()->getContents();
+        $response3 = $client->get('http://127.0.0.1/redis/testCache')->getResult()->getBody()->getContents();
+        return [$response, $response2, $response3];
+    }
+}
\ No newline at end of file
diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php
index b7798cb6..dc0ef1db 100644
--- a/app/Controllers/RedisController.php
+++ b/app/Controllers/RedisController.php
@@ -34,7 +34,7 @@ public function testCache()
 
         $this->redis->incrBy("count2", 2);
 
-        return [$result, $name, $this->redis->get('count'), $this->redis->get('count2')];
+        return [$result, $name, $this->redis->get('count'), $this->redis->get('count2'), '3'];
     }
 
     public function testRedis()
diff --git a/config/properties/db.php b/config/properties/db.php
index 9754c909..d8721cc0 100644
--- a/config/properties/db.php
+++ b/config/properties/db.php
@@ -10,9 +10,6 @@
         'maxActive'   => 8,
         'maxWait'     => 8,
         'timeout'     => 8,
-        'balancer'    => 'random',
-        'useProvider' => false,
-        'provider'    => 'consul',
     ],
 
     'slave' => [
@@ -25,8 +22,5 @@
         'maxActive'   => 8,
         'maxWait'     => 8,
         'timeout'     => 8,
-        'balancer'    => 'random',
-        'useProvider' => false,
-        'provider'    => 'consul',
     ],
 ];
\ No newline at end of file

From 0ed999d7aad8f6d4a69d359f08091416181af461 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sun, 25 Feb 2018 22:29:48 +0800
Subject: [PATCH 114/643] add http client

---
 app/Tasks/SyncTask.php | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php
index 39859e39..716b9b0b 100644
--- a/app/Tasks/SyncTask.php
+++ b/app/Tasks/SyncTask.php
@@ -103,15 +103,12 @@ public function mysql(){
      */
     public function http()
     {
-        $client = new Client([
-                'base_uri' => 'http://127.0.0.1/index/post?a=b',
-                'timeout'  => 2,
-            ]);
-
-        $result = $client->post('http://127.0.0.1/index/post?a=b')->getResponse();
-        $result2 = $client->get('http://www.baidu.com/');
-        $data['result'] = $result;
-        $data['result2'] = $result2;
+        $client = new Client();
+        $response = $client->get('http://www.swoft.org/')->getResponse()->getBody()->getContents();
+        $response2 = $client->get('http://127.0.0.1/redis/testCache')->getResponse()->getBody()->getContents();
+
+        $data['result1'] = $response;
+        $data['result2'] = $response2;
         return $data;
     }
 

From 778add726537d2da58b940b5f392215cac2b0ccf Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sun, 25 Feb 2018 22:38:23 +0800
Subject: [PATCH 115/643] modify composer

---
 composer.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/composer.json b/composer.json
index 768b11ac..8e66db25 100644
--- a/composer.json
+++ b/composer.json
@@ -60,11 +60,11 @@
         },
         {
           "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-service-governance"
+          "url": "https://github.com/swoft-cloud/swoft-rpc-client"
         },
         {
           "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-http-message"
+          "url": "https://github.com/swoft-cloud/swoft-view"
         },
         {
             "type": "composer",

From d3258fc125250417b4d1f4c0658dc870a95d1eb7 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Sun, 25 Feb 2018 22:48:23 +0800
Subject: [PATCH 116/643] add process respo

---
 composer.json | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/composer.json b/composer.json
index 8e66db25..969aac33 100644
--- a/composer.json
+++ b/composer.json
@@ -62,6 +62,10 @@
           "type": "vcs",
           "url": "https://github.com/swoft-cloud/swoft-rpc-client"
         },
+        {
+          "type": "vcs",
+          "url": "https://github.com/swoft-cloud/swoft-process"
+        },
         {
           "type": "vcs",
           "url": "https://github.com/swoft-cloud/swoft-view"

From 00e02de2d81dd779f3e635109f86185fa6528c07 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Mon, 26 Feb 2018 02:02:55 +0800
Subject: [PATCH 117/643] format and clear up

---
 .env.example  | 12 ++++++------
 composer.json |  8 ++++----
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/.env.example b/.env.example
index ac2fd563..4b5c7711 100644
--- a/.env.example
+++ b/.env.example
@@ -31,7 +31,7 @@ DISPATCH_MODE=2
 LOG_FILE=@runtime/logs/swoole.log
 TASK_WORKER_NUM=1
 
-# the pool of master nodes pool
+# Database Master nodes
 DB_NAME=dbMaster
 DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8
 DB_MAX_IDEL=6
@@ -42,7 +42,7 @@ DB_USE_PROVIDER=false
 DB_BALANCER=random
 DB_PROVIDER=consul
 
-# the pool of slave nodes pool
+# Database Slave nodes
 DB_SLAVE_NAME=dbSlave
 DB_SLAVE_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8
 DB_SLAVE_MAX_IDEL=6
@@ -53,7 +53,7 @@ DB_SLAVE_USE_PROVIDER=false
 DB_SLAVE_BALANCER=random
 DB_SLAVE_PROVIDER=consul
 
-# the pool of redis
+# Redis
 REDIS_NAME=redis
 REDIS_DB=2
 REDIS_URI=127.0.0.1:6379,127.0.0.1:6379
@@ -63,7 +63,7 @@ REDIS_MAX_WAIT=20
 REDIS_TIMEOUT=200
 REDIS_SERIALIZE=1
 
-# the pool of user service
+# User service (demo service)
 USER_POOL_NAME=user
 USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099
 USER_POOL_MAX_IDEL=6
@@ -74,12 +74,12 @@ USER_POOL_USE_PROVIDER=false
 USER_POOL_BALANCER=random
 USER_POOL_PROVIDER=consul
 
-# the breaker of user service
+# User service breaker (demo service)
 USER_BREAKER_FAIL_COUNT = 3
 USER_BREAKER_SUCCESS_COUNT = 6
 USER_BREAKER_DELAY_TIME = 5000
 
-# the provider of consul
+# Consul
 CONSUL_ADDRESS=http://127.0.0.1
 CONSUL_PORT=8500
 CONSUL_REGISTER_ETO=false
diff --git a/composer.json b/composer.json
index 768b11ac..1cdb94e4 100644
--- a/composer.json
+++ b/composer.json
@@ -59,12 +59,12 @@
             "url": "https://github.com/swoft-cloud/swoft-framework"
         },
         {
-          "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-service-governance"
+            "type": "vcs",
+            "url": "https://github.com/swoft-cloud/swoft-service-governance"
         },
         {
-          "type": "vcs",
-          "url": "https://github.com/swoft-cloud/swoft-http-message"
+            "type": "vcs",
+            "url": "https://github.com/swoft-cloud/swoft-http-message"
         },
         {
             "type": "composer",

From 14d868ae54df57693d392276ba7d96824f162526 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Mon, 26 Feb 2018 02:14:34 +0800
Subject: [PATCH 118/643] fixed request header test

---
 test/Cases/IndexControllerTest.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/test/Cases/IndexControllerTest.php b/test/Cases/IndexControllerTest.php
index db6b48e0..5cc5af38 100644
--- a/test/Cases/IndexControllerTest.php
+++ b/test/Cases/IndexControllerTest.php
@@ -54,7 +54,7 @@ public function testIndex()
             $this->assertInstanceOf(Response::class, $response);
             /** @var Response $response */
             $response->assertSuccessful()
-                ->assertHeader('Content-Type', 'application/json')
+                ->assertHeaderContain('Content-Type', 'application/json')
                 ->assertSee('Swoft')
                 ->assertSeeText('New Generation of PHP Framework')
                 ->assertDontSee('Swoole')
@@ -67,12 +67,12 @@ public function testIndex()
         };
         // Json model
         $response = $this->request('GET', '/', [], parent::ACCEPT_JSON);
-        $response->assertHeader('Content-Type', parent::ACCEPT_JSON);
+        $response->assertHeaderContain('Content-Type', parent::ACCEPT_JSON);
         $jsonAssert($response);
 
         // Raw model
         $response = $this->request('GET', '/', [], parent::ACCEPT_RAW);
-        $response->assertHeader('Content-Type', parent::ACCEPT_JSON);
+        $response->assertHeaderContain('Content-Type', parent::ACCEPT_JSON);
         $jsonAssert($response);
 
         // View model
@@ -81,7 +81,7 @@ public function testIndex()
             ->assertSee($expectedResult['name'])
             ->assertSee($expectedResult['notes'][0])
             ->assertSee($expectedResult['notes'][1])
-            ->assertHeader('Content-Type', 'text/html');
+            ->assertHeaderContain('Content-Type', 'text/html');
 
         // absolutePath
         $response = $this->request('GET', '/index/absolutePath', [], parent::ACCEPT_VIEW);

From 2ac18f0137759baffb94f367c99f64ffbad03aff Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Mon, 26 Feb 2018 02:15:53 +0800
Subject: [PATCH 119/643] add latest HTTPClient use cases

---
 app/Controllers/HttpClientController.php | 25 ++++++++++++++++++++++++
 app/Controllers/HttpController.php       | 24 -----------------------
 2 files changed, 25 insertions(+), 24 deletions(-)
 create mode 100644 app/Controllers/HttpClientController.php
 delete mode 100644 app/Controllers/HttpController.php

diff --git a/app/Controllers/HttpClientController.php b/app/Controllers/HttpClientController.php
new file mode 100644
index 00000000..d567210f
--- /dev/null
+++ b/app/Controllers/HttpClientController.php
@@ -0,0 +1,25 @@
+get('https://www.swoft.org/')->getResult();
+        $result2 = $client->get('https://www.swoft.org/')->getResponse()->getBody()->getContents();
+        return compact('result', 'result2');
+    }
+}
\ No newline at end of file
diff --git a/app/Controllers/HttpController.php b/app/Controllers/HttpController.php
deleted file mode 100644
index c1bc0061..00000000
--- a/app/Controllers/HttpController.php
+++ /dev/null
@@ -1,24 +0,0 @@
-get('http://www.swoft.org/')->getResponse()->getBody()->getContents();
-        $response2 = $client->get('http://127.0.0.1/redis/testCache')->getResponse()->getBody()->getContents();
-        $response3 = $client->get('http://127.0.0.1/redis/testCache')->getResult()->getBody()->getContents();
-        return [$response, $response2, $response3];
-    }
-}
\ No newline at end of file

From 158533f5a7c993e49840a6a5b17eb1b2980ed394 Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Mon, 26 Feb 2018 21:06:01 +0800
Subject: [PATCH 120/643] Update Dockerfile

Update maintainer email
---
 Dockerfile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 46927a92..0082d47e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 FROM php:7.1
 
-MAINTAINER huangzhhui 
+MAINTAINER huangzhhui 
 
 RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
     && echo 'Asia/Shanghai' > /etc/timezone
@@ -10,7 +10,6 @@ RUN apt-get update \
         curl \
         wget \
         git \
-        vim \
         zip \
         libz-dev \
         libssl-dev \
@@ -31,6 +30,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.
         && ldconfig \
     ) \
     && rm -r hiredis
+    
 RUN wget https://github.com/swoole/swoole-src/archive/v2.1.0.tar.gz -O swoole.tar.gz \
     && mkdir -p swoole \
     && tar -xf swoole.tar.gz -C swoole --strip-components=1 \
@@ -54,4 +54,4 @@ RUN composer install --no-dev \
 
 EXPOSE 80
 
-CMD ["php", "/var/www/swoft/bin/swoft", "start"]
\ No newline at end of file
+CMD ["php", "/var/www/swoft/bin/swoft", "start"]

From 7a5fb27a4d4e4e984f34b489fbb58d5b1ebffd35 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Tue, 27 Feb 2018 23:52:43 +0800
Subject: [PATCH 121/643] Update HttpClient namespace

---
 app/Controllers/HttpClientController.php | 2 +-
 app/Tasks/SyncTask.php                   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/Controllers/HttpClientController.php b/app/Controllers/HttpClientController.php
index d567210f..f1d06128 100644
--- a/app/Controllers/HttpClientController.php
+++ b/app/Controllers/HttpClientController.php
@@ -2,7 +2,7 @@
 
 namespace App\Controllers;
 
-use Swoft\Http\Client;
+use Swoft\HttpClient\Client;
 use Swoft\Http\Server\Bean\Annotation\Controller;
 
 /**
diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php
index 716b9b0b..f9ca1554 100644
--- a/app/Tasks/SyncTask.php
+++ b/app/Tasks/SyncTask.php
@@ -6,7 +6,7 @@
 use App\Models\Entity\User;
 use Swoft\App;
 use Swoft\Bean\Annotation\Inject;
-use Swoft\Http\Client;
+use Swoft\HttpClient\Client;
 use Swoft\Rpc\Client\Bean\Annotation\Reference;
 use Swoft\Task\Bean\Annotation\Scheduled;
 use Swoft\Task\Bean\Annotation\Task;

From b5ff0fb30d0984cfef6f4d45d8089db34d874336 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Wed, 28 Feb 2018 00:49:19 +0800
Subject: [PATCH 122/643] add orm

---
 app/Controllers/OrmController.php | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php
index 8b8d7afc..73a0cb71 100644
--- a/app/Controllers/OrmController.php
+++ b/app/Controllers/OrmController.php
@@ -200,9 +200,9 @@ public function find()
      */
     public function arFindId()
     {
-        $result = User::findById(425)->getResult();
+        $result = User::findById(720)->getResult();
 
-        $query = User::findById(426);
+        $query = User::findById(720);
 
         /* @var User $user */
         $user = $query->getResult(User::class);
@@ -279,21 +279,28 @@ public function ts()
         $user->setDesc('this my desc');
         $user->setAge(mt_rand(1, 100));
 
+        $user2 = new User();
+        $user2->setName('stelin');
+        $user2->setSex(1);
+        $user2->setDesc('this my desc');
+        $user2->setAge(mt_rand(1, 100));
+
         $count = new Count();
         $count->setFans(mt_rand(1, 1000));
         $count->setFollows(mt_rand(1, 1000));
 
         $em = EntityManager::create();
+        $re = $user2->save()->getResult();
         $em->beginTransaction();
+
         $uid = $em->save($user)->getResult();
         $count->setUid($uid);
 
         $result = $em->save($count)->getResult();
-        if ($result === false) {
-            $em->rollback();
-        } else {
-            $em->commit();
-        }
+
+        $result2 = $user2->save()->getResult();
+        $em->rollback();
+//        $em->commit();
         $em->close();
 
         return [$uid, $result];

From 61d8fe33220541e9b5e831392dd41c715dcb464a Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Wed, 28 Feb 2018 23:11:49 +0800
Subject: [PATCH 123/643] modify pool params

---
 .env.example                       | 18 +++++++++++++-----
 app/Pool/Config/UserPoolConfig.php | 22 +++++++++++++++++++---
 config/properties/cache.php        |  4 +++-
 config/properties/db.php           |  8 ++++++--
 config/properties/service.php      | 17 ++++++++++++++++-
 5 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/.env.example b/.env.example
index 6ca88adc..4dbcec91 100644
--- a/.env.example
+++ b/.env.example
@@ -37,33 +37,41 @@ DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306
 DB_MAX_IDEL=6
 DB_MAX_ACTIVE=10
 DB_MAX_WAIT=20
+DB_MAX_WAIT_TIME=3
+DB_MAX_IDLE_TIME=60
 DB_TIMEOUT=200
 
 # Database Slave nodes
 DB_SLAVE_NAME=dbSlave
 DB_SLAVE_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8
-DB_SLAVE_MAX_IDEL=6
+DB_SLAVE_MIN_ACTIVE=5
 DB_SLAVE_MAX_ACTIVE=10
 DB_SLAVE_MAX_WAIT=20
-DB_SLAVE_TIMEOUT=200
+DB_SLAVE_MAX_WAIT_TIME=3
+DB_SLAVE_MAX_IDLE_TIME=60
+DB_SLAVE_TIMEOUT=3
 
 # Redis
 REDIS_NAME=redis
 REDIS_DB=2
 REDIS_URI=127.0.0.1:6379,127.0.0.1:6379
-REDIS_MAX_IDEL=6
+REDIS_MIN_ACTIVE=5
 REDIS_MAX_ACTIVE=10
 REDIS_MAX_WAIT=20
-REDIS_TIMEOUT=200
+REDIS_MAX_WAIT_TIME=3
+REDIS_MAX_IDLE_TIME=60
+REDIS_TIMEOUT=3
 REDIS_SERIALIZE=1
 
 # User service (demo service)
 USER_POOL_NAME=user
 USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099
-USER_POOL_MAX_IDEL=6
+USER_POOL_MIN_ACTIVE=5
 USER_POOL_MAX_ACTIVE=10
 USER_POOL_MAX_WAIT=20
 USER_POOL_TIMEOUT=200
+USER_POOL_MAX_WAIT_TIME=3
+USER_POOL_MAX_IDLE_TIME=60
 USER_POOL_USE_PROVIDER=false
 USER_POOL_BALANCER=random
 USER_POOL_PROVIDER=consul
diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php
index 130e44cc..27500e46 100644
--- a/app/Pool/Config/UserPoolConfig.php
+++ b/app/Pool/Config/UserPoolConfig.php
@@ -29,12 +29,12 @@ class UserPoolConfig extends PoolProperties
     protected $name = "";
 
     /**
-     * the maximum number of idle connections
+     * Minimum active number of connections
      *
-     * @Value(name="${config.service.user.maxIdel}", env="${USER_POOL_MAX_IDEL}")
+     * @Value(name="${config.service.user.minActive}", env="${USER_POOL_MIN_ACTIVE}")
      * @var int
      */
-    protected $maxIdel = 6;
+    protected $minActive = 5;
 
     /**
      * the maximum number of active connections
@@ -52,6 +52,22 @@ class UserPoolConfig extends PoolProperties
      */
     protected $maxWait = 100;
 
+    /**
+     * Maximum waiting time
+     *
+     * @Value(name="${config.service.user.maxWaitTime}", env="${USER_POOL_MAX_WAIT_TIME}")
+     * @var int
+     */
+    protected $maxWaitTime = 3;
+
+    /**
+     * Maximum idle time
+     *
+     * @Value(name="${config.service.user.maxIdleTime}", env="${USER_POOL_MAX_IDLE_TIME}")
+     * @var int
+     */
+    protected $maxIdleTime = 60;
+
     /**
      * the time of connect timeout
      *
diff --git a/config/properties/cache.php b/config/properties/cache.php
index 352fe8dd..175db1c1 100644
--- a/config/properties/cache.php
+++ b/config/properties/cache.php
@@ -6,9 +6,11 @@
             '127.0.0.1:6379',
             '127.0.0.1:6379',
         ],
-        'maxIdel'     => 8,
+        'minActive'   => 8,
         'maxActive'   => 8,
         'maxWait'     => 8,
+        'maxWaitTime' => 3,
+        'maxIdleTime' => 60,
         'timeout'     => 8,
         'db'          => 1,
         'serialize'   => 0,
diff --git a/config/properties/db.php b/config/properties/db.php
index d8721cc0..2ab37d3f 100644
--- a/config/properties/db.php
+++ b/config/properties/db.php
@@ -6,10 +6,12 @@
             '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
             '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
         ],
-        'maxIdel'     => 8,
+        'minActive'   => 8,
         'maxActive'   => 8,
         'maxWait'     => 8,
         'timeout'     => 8,
+        'maxIdleTime' => 60,
+        'maxWaitTime' => 3,
     ],
 
     'slave' => [
@@ -18,9 +20,11 @@
             '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
             '127.0.0.1:3306/test?user=root&password=123456&charset=utf8',
         ],
-        'maxIdel'     => 8,
+        'minActive'   => 8,
         'maxActive'   => 8,
         'maxWait'     => 8,
         'timeout'     => 8,
+        'maxIdleTime' => 60,
+        'maxWaitTime' => 3,
     ],
 ];
\ No newline at end of file
diff --git a/config/properties/service.php b/config/properties/service.php
index 05e0b10e..e6d7df42 100644
--- a/config/properties/service.php
+++ b/config/properties/service.php
@@ -1,4 +1,19 @@
  [
+        'name'        => 'redis',
+        'uri'         => [
+            '127.0.0.1:8099',
+            '127.0.0.1:8099',
+        ],
+        'minActive'   => 8,
+        'maxActive'   => 8,
+        'maxWait'     => 8,
+        'maxWaitTime' => 3,
+        'maxIdleTime' => 60,
+        'timeout'     => 8,
+        'useProvider' => false,
+        'balancer' => 'random',
+        'provider' => 'consul',
+    ]
 ];
\ No newline at end of file

From 897240a67ee4f6e4c308271ee3705cab41d6d749 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Wed, 28 Feb 2018 23:34:54 +0800
Subject: [PATCH 124/643] modify .env.example

---
 .env.example | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.env.example b/.env.example
index 4dbcec91..898ec0a7 100644
--- a/.env.example
+++ b/.env.example
@@ -34,11 +34,13 @@ TASK_WORKER_NUM=1
 # Database Master nodes
 DB_NAME=dbMaster
 DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8
-DB_MAX_IDEL=6
+DB_MIN_ACTIVE=5
 DB_MAX_ACTIVE=10
 DB_MAX_WAIT=20
 DB_MAX_WAIT_TIME=3
 DB_MAX_IDLE_TIME=60
+DB_MAX_WAIT_TIME=3
+DB_MAX_IDLE_TIME=60
 DB_TIMEOUT=200
 
 # Database Slave nodes

From 9dd942271b8bd6e36bb17e5efec47101f5ea8289 Mon Sep 17 00:00:00 2001
From: kcloze <460309735@qq.com>
Date: Thu, 1 Mar 2018 23:11:32 +0800
Subject: [PATCH 125/643] add docker compose file and edit readme file

---
 README.md           |  6 +++++-
 docker-compose.yaml | 11 +++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 docker-compose.yaml

diff --git a/README.md b/README.md
index a3032cc4..339b463f 100644
--- a/README.md
+++ b/README.md
@@ -64,9 +64,13 @@ QQ 交流群: 548173319
 * `composer create-project swoft/swoft swoft dev-master`
 
 ## Docker 安装
-
+* `cd swoft`
 * `docker run -p 80:80 swoft/swoft`
 
+## Docker-compose 安装
+* `cd swoft`
+* `docker-compose up -d`
+
 # 配置
 
 若在执行 `composer install` 的时候由程序自动复制环境变量配置文件失败,则可手动复制项目根目录的 `.env.example` 并命名为 `.env`,注意在执行 `composer update` 时并不会触发相关的复制操作
diff --git a/docker-compose.yaml b/docker-compose.yaml
new file mode 100644
index 00000000..530bdca4
--- /dev/null
+++ b/docker-compose.yaml
@@ -0,0 +1,11 @@
+version: '0.2'
+services:
+    swoft-dev:
+        image: swoft/swoft:latest
+        ports:
+            - "801:80"
+        volumes:
+            - ./:/var/www/swoft
+        stdin_open: true
+        tty: true
+        command: php /var/www/swoft/bin/swoft start

From c2adc92baf1647078743762006471435c86096cf Mon Sep 17 00:00:00 2001
From: kcloze <460309735@qq.com>
Date: Thu, 1 Mar 2018 23:27:23 +0800
Subject: [PATCH 126/643] change docker compose filename

---
 docker-compose.yaml => docker-compose.yml | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename docker-compose.yaml => docker-compose.yml (100%)

diff --git a/docker-compose.yaml b/docker-compose.yml
similarity index 100%
rename from docker-compose.yaml
rename to docker-compose.yml

From 89cbbc35cbb686640e3b03bde8e671bd9b024c03 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Fri, 2 Mar 2018 00:10:33 +0800
Subject: [PATCH 127/643] fallback

---
 app/Controllers/RpcController.php    | 36 +++++++++++++++++++++++++++-
 app/Fallback/DemoServiceFallback.php | 33 +++++++++++++++++++++++++
 config/properties/app.php            |  1 +
 3 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 app/Fallback/DemoServiceFallback.php

diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php
index f9d484f0..7bee9f4e 100644
--- a/app/Controllers/RpcController.php
+++ b/app/Controllers/RpcController.php
@@ -17,7 +17,7 @@ class RpcController
 {
 
     /**
-     * @Reference("user")
+     * @Reference(name="user", fallback="demoFallback")
      *
      * @var DemoInterface
      */
@@ -42,6 +42,40 @@ class RpcController
      */
     private $logic;
 
+
+    /**
+     * @return array
+     */
+    public function fallback()
+    {
+        $result1  = $this->demoService->getUser('11');
+        $result2  = $this->demoService->getUsers(['1','2']);
+        $result3  = $this->demoService->getUserByCond(1, 2, 'boy', 1.6);
+
+        return [
+            $result1,
+            $result2,
+            $result3,
+        ];
+    }
+
+    /**
+     * @return array
+     */
+    public function deferFallback()
+    {
+        $result1  = $this->demoService->deferGetUser('11')->getResult();
+        $result2  = $this->demoService->deferGetUsers(['1','2'])->getResult();
+        $result3  = $this->demoService->deferGetUserByCond(1, 2, 'boy', 1.6)->getResult();
+
+        return [
+            'defer',
+            $result1,
+            $result2,
+            $result3,
+        ];
+    }
+
     /**
      * @RequestMapping(route="call")
      * @return array
diff --git a/app/Fallback/DemoServiceFallback.php b/app/Fallback/DemoServiceFallback.php
new file mode 100644
index 00000000..5ba02d3e
--- /dev/null
+++ b/app/Fallback/DemoServiceFallback.php
@@ -0,0 +1,33 @@
+ [
         'sourceLanguage' => '@root/resources/messages/',

From 952acf095de028d6b0b93bef20c0476336023e6a Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Fri, 2 Mar 2018 00:53:20 +0800
Subject: [PATCH 128/643] Update docker-compose.yml

---
 docker-compose.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docker-compose.yml b/docker-compose.yml
index 530bdca4..7897b782 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,9 +1,9 @@
-version: '0.2'
+version: '2'
 services:
     swoft-dev:
         image: swoft/swoft:latest
         ports:
-            - "801:80"
+            - "80:80"
         volumes:
             - ./:/var/www/swoft
         stdin_open: true

From 5f4d4db8a4cd0b3681bea0c48bb0005246f066b3 Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Fri, 2 Mar 2018 00:53:51 +0800
Subject: [PATCH 129/643] Update docker-compose.yml

---
 docker-compose.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docker-compose.yml b/docker-compose.yml
index 7897b782..c44f15f0 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,6 +1,6 @@
 version: '2'
 services:
-    swoft-dev:
+    swoft:
         image: swoft/swoft:latest
         ports:
             - "80:80"

From 8660e6ed3b485851ac78791eef70dc2c249adfea Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Fri, 2 Mar 2018 00:55:14 +0800
Subject: [PATCH 130/643] Update README.md

---
 README.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 339b463f..5d544f49 100644
--- a/README.md
+++ b/README.md
@@ -64,12 +64,11 @@ QQ 交流群: 548173319
 * `composer create-project swoft/swoft swoft dev-master`
 
 ## Docker 安装
-* `cd swoft`
 * `docker run -p 80:80 swoft/swoft`
 
 ## Docker-compose 安装
 * `cd swoft`
-* `docker-compose up -d`
+* `docker-compose up`
 
 # 配置
 

From 3098adae849638ac2deb3461d4a4544225276d4e Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Fri, 2 Mar 2018 00:56:01 +0800
Subject: [PATCH 131/643] Update README.md

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 5d544f49..3bdbf524 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ QQ 交流群: 548173319
 ## Docker 安装
 * `docker run -p 80:80 swoft/swoft`
 
-## Docker-compose 安装
+## Docker-ompose 安装
 * `cd swoft`
 * `docker-compose up`
 

From 4ca605eb1d5b2b0afa1812f675116d225040c3ad Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Fri, 2 Mar 2018 00:56:28 +0800
Subject: [PATCH 132/643] Update README.md

---
 README.md | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index 3bdbf524..62709615 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,7 @@ QQ 交流群: 548173319
 ## Docker 安装
 * `docker run -p 80:80 swoft/swoft`
 
-## Docker-ompose 安装
+## Docker-Compose 安装
 * `cd swoft`
 * `docker-compose up`
 
@@ -180,11 +180,4 @@ php bin/swoft rpc:stop
 [更新日志](changelog.md)
 
 # 协议
-Swoft 的开源协议为 Apache-2.0,详情参见[LICENSE](LICENSE)。
-
-
-
-
-
-
-
+Swoft 的开源协议为 Apache-2.0,详情参见[LICENSE](LICENSE)

From 7fb516a7639143e75550637de59085b0fb915296 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Fri, 2 Mar 2018 01:02:56 +0800
Subject: [PATCH 133/643] add PACKAGE_MAX_LENGTH server setting

---
 .env.example      | 1 +
 config/server.php | 1 +
 2 files changed, 2 insertions(+)

diff --git a/.env.example b/.env.example
index 6ca88adc..c703a18d 100644
--- a/.env.example
+++ b/.env.example
@@ -30,6 +30,7 @@ DAEMONIZE=0
 DISPATCH_MODE=2
 LOG_FILE=@runtime/logs/swoole.log
 TASK_WORKER_NUM=1
+PACKAGE_MAX_LENGTH=2048
 
 # Database Master nodes
 DB_NAME=dbMaster
diff --git a/config/server.php b/config/server.php
index 40f68c95..3e3a776d 100644
--- a/config/server.php
+++ b/config/server.php
@@ -32,6 +32,7 @@
         'dispatch_mode'         => env('DISPATCH_MODE', 2),
         'log_file'              => env('LOG_FILE', '@runtime/logs/swoole.log'),
         'task_worker_num'       => env('TASK_WORKER_NUM', 1),
+        'package_max_length'    => env('PACKAGE_MAX_LENGTH', 2048),
         'upload_tmp_dir'        => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'),
         'document_root'         => env('DOCUMENT_ROOT', BASE_PATH . '/public'),
         'enable_static_handler' => env('ENABLE_STATIC_HANDLER', true),

From d8fac75284b68672995e252d3acc2b468c2b2db7 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Fri, 2 Mar 2018 01:38:01 +0800
Subject: [PATCH 134/643] Modify feature list

---
 README.md                                | 21 +++++++++++----------
 app/Controllers/HttpClientController.php |  1 +
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index a3032cc4..a67c1666 100644
--- a/README.md
+++ b/README.md
@@ -17,28 +17,29 @@
 
 - 基于 Swoole 扩展
 - 内置协程网络服务器
-- MVC 分层设计
-- 高性能路由
 - 强大的 AOP (面向切面编程)
-- 灵活的注解功能
+- 灵活完善的注解功能
 - 全局的依赖注入容器
 - 基于 PSR-7 的 HTTP 消息实现
 - 基于 PSR-14 的事件管理器
 - 基于 PSR-15 的中间件
 - 基于 PSR-16 的缓存设计
 - 可扩展的高性能 RPC
-- RESTful 支持
-- 国际化(i18n)支持
-- 快速灵活的参数验证器
-- 完善的服务治理,熔断、降级、负载、注册与发现
-- 通用连接池 Mysql、Redis、RPC
+- 完善的服务治理,熔断,降级,负载,注册与发现
 - 数据库 ORM
+- 通用连接池
+- 协程 Mysql, Redis, RPC, HTTP 客户端
+- 协程和同步阻塞客户端无缝自动切换
 - 协程、异步任务投递
 - 自定义用户进程
-- 协程和同步阻塞客户端无缝自动切换
+- RESTful 支持
+- 国际化(i18n)支持
+- 高性能路由
+- 快速灵活的参数验证器
 - 别名机制
-- 跨平台热更新自动 Reload
 - 强大的日志系统
+- 跨平台热更新自动 Reload
+
 
 # 文档
 [**中文文档**](https://doc.swoft.org)
diff --git a/app/Controllers/HttpClientController.php b/app/Controllers/HttpClientController.php
index f1d06128..0c7fcc17 100644
--- a/app/Controllers/HttpClientController.php
+++ b/app/Controllers/HttpClientController.php
@@ -12,6 +12,7 @@ class HttpClientController
 {
     /**
      * @return array
+     * @throws \Swoft\HttpClient\Exception\RuntimeException
      * @throws \RuntimeException
      * @throws \InvalidArgumentException
      */

From 4c39b7f52573fbac6b6ef3bc0857931a5ca3a836 Mon Sep 17 00:00:00 2001
From: huangzhhui 
Date: Fri, 2 Mar 2018 02:06:31 +0800
Subject: [PATCH 135/643] add Redis extension

---
 Dockerfile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 0082d47e..f654172d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -19,6 +19,8 @@ RUN curl -sS https://getcomposer.org/installer | php \
     && mv composer.phar /usr/local/bin/composer \
     && composer self-update --clean-backups
 
+RUN pecl install redis && docker-php-ext-enable redis && pecl clear-cache
+
 RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz \
     && mkdir -p hiredis \
     && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \

From 5089b0ac34361b87a6d8eeecdc44451bdfc03390 Mon Sep 17 00:00:00 2001
From: lixiaopei 
Date: Fri, 2 Mar 2018 10:23:38 +0800
Subject: [PATCH 136/643] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20php-cs-fixer?=
 =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6=EF=BC=8C=E7=BB=9F=E4=B8=80?=
 =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .php_cs | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)
 create mode 100644 .php_cs

diff --git a/.php_cs b/.php_cs
new file mode 100644
index 00000000..48d22525
--- /dev/null
+++ b/.php_cs
@@ -0,0 +1,46 @@
+
+For the full copyright and license information, please view the LICENSE
+file that was distributed with this source code.
+EOF;
+
+return PhpCsFixer\Config::create()
+    ->setRiskyAllowed(true)
+    ->setRules([
+        '@Symfony'                   => true,
+        '@Symfony:risky'             => true,
+        'array_syntax'               => ['syntax' => 'short'],
+        'combine_consecutive_unsets' => true,
+        // one should use PHPUnit methods to set up expected exception instead of annotations
+        'general_phpdoc_annotation_remove'      => ['expectedException', 'expectedExceptionMessage', 'expectedExceptionMessageRegExp'],
+        'header_comment'                        => ['header' => $header],
+        'heredoc_to_nowdoc'                     => true,
+        'no_extra_consecutive_blank_lines'      => ['break', 'continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block'],
+        'no_unreachable_default_argument_value' => true,
+        'no_useless_else'                       => true,
+        'no_useless_return'                     => true,
+        'ordered_class_elements'                => true,
+        'ordered_imports'                       => true,
+        'php_unit_strict'                       => true,
+        'phpdoc_add_missing_param_annotation'   => true,
+        'phpdoc_order'                          => true,
+        'psr4'                                  => true,
+        'strict_comparison'                     => false,
+        'strict_param'                          => true,
+        'binary_operator_spaces'                => ['align_double_arrow' => true, 'align_equals' => true],
+        'concat_space'                          => ['spacing' => 'one'],
+        'no_empty_statement'                    => true,
+        'simplified_null_return'                => true,
+        'no_extra_consecutive_blank_lines'      => true,
+        'pre_increment'                         => false
+    ])
+    ->setFinder(
+        PhpCsFixer\Finder::create()
+            ->exclude('vendor')
+            ->exclude('runtime')
+            ->in(__DIR__)
+    )
+    ->setUsingCache(false)
+;
\ No newline at end of file

From 03534785d57e5df83e3ec9b79c0a7df7eacaeb20 Mon Sep 17 00:00:00 2001
From: lixiaopei 
Date: Fri, 2 Mar 2018 10:29:01 +0800
Subject: [PATCH 137/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9php-cs-fixer=E6=A8=A1?=
 =?UTF-8?q?=E6=9D=BF=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .php_cs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/.php_cs b/.php_cs
index 48d22525..4587fe8b 100644
--- a/.php_cs
+++ b/.php_cs
@@ -1,6 +1,6 @@
 
 For the full copyright and license information, please view the LICENSE
 file that was distributed with this source code.
@@ -13,7 +13,6 @@ return PhpCsFixer\Config::create()
         '@Symfony:risky'             => true,
         'array_syntax'               => ['syntax' => 'short'],
         'combine_consecutive_unsets' => true,
-        // one should use PHPUnit methods to set up expected exception instead of annotations
         'general_phpdoc_annotation_remove'      => ['expectedException', 'expectedExceptionMessage', 'expectedExceptionMessageRegExp'],
         'header_comment'                        => ['header' => $header],
         'heredoc_to_nowdoc'                     => true,

From 8d9186dbd062a5ad663548b115f1ec3aa6192145 Mon Sep 17 00:00:00 2001
From: daydaygo <1252409767@qq.com>
Date: Mon, 5 Mar 2018 18:01:26 +0800
Subject: [PATCH 138/643] fix 'model -> mode'

---
 .env.example      |  4 ++--
 composer.json     | 36 ++++++++++++++++++++++++++++++------
 config/server.php |  4 ++--
 3 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/.env.example b/.env.example
index 4d819def..08eedddb 100644
--- a/.env.example
+++ b/.env.example
@@ -8,13 +8,13 @@ AUTO_RELOAD=true
 # HTTP
 HTTP_HOST=0.0.0.0
 HTTP_PORT=80
-HTTP_MODEL=SWOOLE_PROCESS
+HTTP_MODE=SWOOLE_PROCESS
 HTTP_TYPE=SWOOLE_SOCK_TCP
 
 # TCP
 TCP_HOST=0.0.0.0
 TCP_PORT=8099
-TCP_MODEL=SWOOLE_PROCESS
+TCP_MODE=SWOOLE_PROCESS
 TCP_TYPE=SWOOLE_SOCK_TCP
 TCP_PACKAGE_MAX_LENGTH=2048
 TCP_OPEN_EOF_CHECK=false
diff --git a/composer.json b/composer.json
index 8655a7f9..1d98f65a 100644
--- a/composer.json
+++ b/composer.json
@@ -56,27 +56,51 @@
     "repositories": [
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-framework"
+            "url": "git@github.com:daydaygo/swoft-framework.git"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-service-governance"
+            "url": "git@github.com:daydaygo/swoft-cache.git"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-rpc-client"
+            "url": "git@github.com:daydaygo/swoft-session.git"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-http-message"
+            "url": "git@github.com:daydaygo/swoft-db.git"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-process"
+            "url": "git@github.com:daydaygo/swoft-task.git"
+        },
+        {
+            "type": "vcs",
+            "url": "git@github.com:daydaygo/swoft-view.git"
+        },
+        {
+            "type": "vcs",
+            "url": "git@github.com:daydaygo/swoft-http-server.git"
+        },
+        {
+            "type": "vcs",
+            "url": "git@github.com:daydaygo/swoft-rpc-server.git"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-view"
+            "url": "git@github.com:daydaygo/swoft-rpc-client.git"
+        },
+        {
+            "type": "vcs",
+            "url": "https://github.com/swoft-cloud/swoft-service-governance"
+        },
+        {
+            "type": "vcs",
+            "url": "https://github.com/swoft-cloud/swoft-http-message"
+        },
+        {
+            "type": "vcs",
+            "url": "https://github.com/swoft-cloud/swoft-process"
         },
         {
             "type": "composer",
diff --git a/config/server.php b/config/server.php
index 3e3a776d..9cd4fe82 100644
--- a/config/server.php
+++ b/config/server.php
@@ -10,7 +10,7 @@
     'tcp'     => [
         'host'               => env('TCP_HOST', '0.0.0.0'),
         'port'               => env('TCP_PORT', 8099),
-        'model'              => env('TCP_MODEL', SWOOLE_PROCESS),
+        'mode'              => env('TCP_MODE', SWOOLE_PROCESS),
         'type'               => env('TCP_TYPE', SWOOLE_SOCK_TCP),
         'package_max_length' => env('TCP_PACKAGE_MAX_LENGTH', 2048),
         'open_eof_check'     => env('TCP_OPEN_EOF_CHECK', false),
@@ -18,7 +18,7 @@
     'http'    => [
         'host'  => env('HTTP_HOST', '0.0.0.0'),
         'port'  => env('HTTP_PORT', 80),
-        'model' => env('HTTP_MODEL', SWOOLE_PROCESS),
+        'mode' => env('HTTP_MODE', SWOOLE_PROCESS),
         'type'  => env('HTTP_TYPE', SWOOLE_SOCK_TCP),
     ],
     'crontab' => [

From 8241a77a41e91e13865a1bf2c6452e7e5cbb4b7d Mon Sep 17 00:00:00 2001
From: daydaygo <1252409767@qq.com>
Date: Mon, 5 Mar 2018 18:09:48 +0800
Subject: [PATCH 139/643] update

---
 composer.json | 36 ++++++------------------------------
 1 file changed, 6 insertions(+), 30 deletions(-)

diff --git a/composer.json b/composer.json
index 1d98f65a..8655a7f9 100644
--- a/composer.json
+++ b/composer.json
@@ -56,51 +56,27 @@
     "repositories": [
         {
             "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-framework.git"
+            "url": "https://github.com/swoft-cloud/swoft-framework"
         },
         {
             "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-cache.git"
-        },
-        {
-            "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-session.git"
-        },
-        {
-            "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-db.git"
-        },
-        {
-            "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-task.git"
-        },
-        {
-            "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-view.git"
-        },
-        {
-            "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-http-server.git"
-        },
-        {
-            "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-rpc-server.git"
+            "url": "https://github.com/swoft-cloud/swoft-service-governance"
         },
         {
             "type": "vcs",
-            "url": "git@github.com:daydaygo/swoft-rpc-client.git"
+            "url": "https://github.com/swoft-cloud/swoft-rpc-client"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-service-governance"
+            "url": "https://github.com/swoft-cloud/swoft-http-message"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-http-message"
+            "url": "https://github.com/swoft-cloud/swoft-process"
         },
         {
             "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-process"
+            "url": "https://github.com/swoft-cloud/swoft-view"
         },
         {
             "type": "composer",

From 8585941c5bf2bf83b6dfb10aae2056354085da5f Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Mon, 5 Mar 2018 18:17:28 +0800
Subject: [PATCH 140/643] add sql case

---
 app/Controllers/OrmController.php | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php
index 73a0cb71..6678a94c 100644
--- a/app/Controllers/OrmController.php
+++ b/app/Controllers/OrmController.php
@@ -48,6 +48,15 @@ public function arSave()
         return [$userResult, $countResult, $directUser, $directCount];
     }
 
+    public function test(){
+        $sql = "select * from user";
+        $em = EntityManager::create();
+        $result = $em->createQuery($sql)->execute()->getResult();
+        $em->close();
+
+        return [$result];
+    }
+
     /**
      * EM查找
      */

From ec946b76498086b78ef06c9271bc34d2a0b09634 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Tue, 6 Mar 2018 21:35:50 +0800
Subject: [PATCH 141/643] add consul provider

---
 .env.example                       |  9 +++++----
 app/Pool/Config/UserPoolConfig.php | 11 ++---------
 app/Pool/UserServicePool.php       |  6 ------
 config/beans/base.php              |  2 +-
 config/properties/app.php          |  1 +
 config/properties/provider.php     |  6 +++---
 6 files changed, 12 insertions(+), 23 deletions(-)

diff --git a/.env.example b/.env.example
index 4d819def..24f7f94d 100644
--- a/.env.example
+++ b/.env.example
@@ -4,6 +4,7 @@ PNAME=php-swoft
 TCPABLE=true
 CRONABLE=false
 AUTO_RELOAD=true
+AUTO_REGISTER=false
 
 # HTTP
 HTTP_HOST=0.0.0.0
@@ -87,11 +88,11 @@ USER_BREAKER_DELAY_TIME = 5000
 # Consul
 CONSUL_ADDRESS=http://127.0.0.1
 CONSUL_PORT=8500
+CONSUL_REGISTER_NAME=user
 CONSUL_REGISTER_ETO=false
-CONSUL_REGISTER_SERVICE_ADDRESS=http://127.0.0.1
-CONSUL_REGISTER_SERVICE_PORT=88
+CONSUL_REGISTER_SERVICE_ADDRESS=127.0.0.1
+CONSUL_REGISTER_SERVICE_PORT=8099
 CONSUL_REGISTER_CHECK_NAME=user
 CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099
 CONSUL_REGISTER_CHECK_INTERVAL=10
-CONSUL_REGISTER_CHECK_TIMEOUT=1
-CONSUL_DISCOVERY_NAME=user
\ No newline at end of file
+CONSUL_REGISTER_CHECK_TIMEOUT=1
\ No newline at end of file
diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php
index 27500e46..628fa386 100644
--- a/app/Pool/Config/UserPoolConfig.php
+++ b/app/Pool/Config/UserPoolConfig.php
@@ -4,19 +4,12 @@
 
 use Swoft\Bean\Annotation\Bean;
 use Swoft\Bean\Annotation\Value;
-use Swoft\Sg\BalancerSelector;
 use Swoft\Pool\PoolProperties;
-use Swoft\Sg\ProviderSelector;
 
 /**
  * the config of service user
  *
  * @Bean()
- * @uses      UserPoolConfig
- * @version   2017年12月16日
- * @author    stelin 
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
  */
 class UserPoolConfig extends PoolProperties
 {
@@ -105,7 +98,7 @@ class UserPoolConfig extends PoolProperties
      * @Value(name="${config.service.user.balancer}", env="${USER_POOL_BALANCER}")
      * @var string
      */
-    protected $balancer = BalancerSelector::TYPE_RANDOM;
+    protected $balancer = "";
 
     /**
      * the default provider is consul provider
@@ -113,5 +106,5 @@ class UserPoolConfig extends PoolProperties
      * @Value(name="${config.service.user.provider}", env="${USER_POOL_PROVIDER}")
      * @var string
      */
-    protected $provider = ProviderSelector::TYPE_CONSUL;
+    protected $provider = "";
 }
\ No newline at end of file
diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php
index 25d4e430..34e5d259 100644
--- a/app/Pool/UserServicePool.php
+++ b/app/Pool/UserServicePool.php
@@ -11,12 +11,6 @@
  * the pool of user service
  *
  * @Pool(name="user")
- *
- * @uses      UserServicePool
- * @version   2017年12月14日
- * @author    stelin 
- * @copyright Copyright 2010-2016 swoft software
- * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
  */
 class UserServicePool extends ServicePool
 {
diff --git a/config/beans/base.php b/config/beans/base.php
index 1a7fbf47..d4110c58 100644
--- a/config/beans/base.php
+++ b/config/beans/base.php
@@ -22,5 +22,5 @@
     ],
     'cache'            => [
         'driver' => 'redis',
-    ]
+    ],
 ];
diff --git a/config/properties/app.php b/config/properties/app.php
index 712e83ca..bd48e6bd 100644
--- a/config/properties/app.php
+++ b/config/properties/app.php
@@ -27,4 +27,5 @@
     'cache'        => require __DIR__ . DS . 'cache.php',
     'service'      => require __DIR__ . DS . 'service.php',
     'breaker'      => require __DIR__ . DS . 'breaker.php',
+    'provider'      => require __DIR__ . DS . 'provider.php',
 ];
\ No newline at end of file
diff --git a/config/properties/provider.php b/config/properties/provider.php
index 0d6e59e8..87951522 100644
--- a/config/properties/provider.php
+++ b/config/properties/provider.php
@@ -9,13 +9,13 @@
             'tags'              => [],
             'enableTagOverride' => false,
             'service'           => [
-                'address' => 'http://127.0.0.1/',
-                'port'   => '88',
+                'address' => 'localhost',
+                'port'   => '8099',
             ],
             'check'             => [
                 'id'       => '',
                 'name'     => '',
-                'tcp'      => 'localhost:22',
+                'tcp'      => 'localhost:8099',
                 'interval' => 10,
                 'timeout'  => 1,
             ],

From 589067568afc46c8aecf8fd5d9f500623b683559 Mon Sep 17 00:00:00 2001
From: lilin <794774870@qq.com>
Date: Tue, 6 Mar 2018 22:43:55 +0800
Subject: [PATCH 142/643] modify composer

---
 composer.json | 62 ++++++++++++++++-----------------------------------
 1 file changed, 19 insertions(+), 43 deletions(-)

diff --git a/composer.json b/composer.json
index 8655a7f9..1a39e870 100644
--- a/composer.json
+++ b/composer.json
@@ -10,25 +10,25 @@
     "license": "Apache-2.0",
     "require": {
         "php": ">=7.0",
-        "swoft/framework": "dev-master",
-        "swoft/rpc": "dev-master",
-        "swoft/rpc-server": "dev-master",
-        "swoft/rpc-client": "dev-master",
-        "swoft/http-server": "dev-master",
-        "swoft/http-client": "dev-master",
-        "swoft/task": "dev-master",
-        "swoft/http-message": "dev-master",
-        "swoft/view": "dev-master",
-        "swoft/db": "dev-master",
-        "swoft/cache": "dev-master",
-        "swoft/redis": "dev-master",
-        "swoft/console": "dev-master",
-        "swoft/devtool": "dev-master",
-        "swoft/session": "dev-master",
-        "swoft/i18n": "dev-master",
-        "swoft/process": "dev-master",
-        "swoft/memory": "dev-master",
-        "swoft/service-governance": "dev-master"
+        "swoft/framework": "^1.0",
+        "swoft/rpc": "^1.0",
+        "swoft/rpc-server": "^1.0",
+        "swoft/rpc-client": "^1.0",
+        "swoft/http-server": "^1.0",
+        "swoft/http-client": "^1.0",
+        "swoft/task": "^1.0",
+        "swoft/http-message": "^1.0",
+        "swoft/view": "^1.0",
+        "swoft/db": "^1.0",
+        "swoft/cache": "^1.0",
+        "swoft/redis": "^1.0",
+        "swoft/console": "^1.0",
+        "swoft/devtool": "^1.0",
+        "swoft/session": "^1.0",
+        "swoft/i18n": "^1.0",
+        "swoft/process": "^1.0",
+        "swoft/memory": "^1.0",
+        "swoft/service-governance": "^1.0"
     },
     "require-dev": {
         "eaglewu/swoole-ide-helper": "dev-master",
@@ -54,30 +54,6 @@
         "test": "./vendor/bin/phpunit -c phpunit.xml"
     },
     "repositories": [
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-framework"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-service-governance"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-rpc-client"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-http-message"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-process"
-        },
-        {
-            "type": "vcs",
-            "url": "https://github.com/swoft-cloud/swoft-view"
-        },
         {
             "type": "composer",
             "url": "https://packagist.phpcomposer.com/"

From bd1edef4bcc75fcb6817a043f0f8a1004b047b68 Mon Sep 17 00:00:00 2001
From: kcloze <460309735@qq.com>
Date: Wed, 7 Mar 2018 00:17:05 +0800
Subject: [PATCH 143/643] =?UTF-8?q?=E5=88=A0=E9=99=A4=E8=87=AA=E5=AE=9A?=
 =?UTF-8?q?=E4=B9=89=E7=BC=96=E7=A0=81=E8=A7=84=E8=8C=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .php_cs | 59 +++++++++++++++++++++++++++++----------------------------
 1 file changed, 30 insertions(+), 29 deletions(-)

diff --git a/.php_cs b/.php_cs
index 4587fe8b..f56582fd 100644
--- a/.php_cs
+++ b/.php_cs
@@ -1,4 +1,5 @@
 
@@ -7,34 +8,34 @@ file that was distributed with this source code.
 EOF;
 
 return PhpCsFixer\Config::create()
-    ->setRiskyAllowed(true)
-    ->setRules([
-        '@Symfony'                   => true,
-        '@Symfony:risky'             => true,
-        'array_syntax'               => ['syntax' => 'short'],
-        'combine_consecutive_unsets' => true,
-        'general_phpdoc_annotation_remove'      => ['expectedException', 'expectedExceptionMessage', 'expectedExceptionMessageRegExp'],
-        'header_comment'                        => ['header' => $header],
-        'heredoc_to_nowdoc'                     => true,
-        'no_extra_consecutive_blank_lines'      => ['break', 'continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block'],
-        'no_unreachable_default_argument_value' => true,
-        'no_useless_else'                       => true,
-        'no_useless_return'                     => true,
-        'ordered_class_elements'                => true,
-        'ordered_imports'                       => true,
-        'php_unit_strict'                       => true,
-        'phpdoc_add_missing_param_annotation'   => true,
-        'phpdoc_order'                          => true,
-        'psr4'                                  => true,
-        'strict_comparison'                     => false,
-        'strict_param'                          => true,
-        'binary_operator_spaces'                => ['align_double_arrow' => true, 'align_equals' => true],
-        'concat_space'                          => ['spacing' => 'one'],
-        'no_empty_statement'                    => true,
-        'simplified_null_return'                => true,
-        'no_extra_consecutive_blank_lines'      => true,
-        'pre_increment'                         => false
-    ])
+    // ->setRiskyAllowed(true)
+    // ->setRules([
+    //     '@Symfony'                   => true,
+    //     '@Symfony:risky'             => true,
+    //     'array_syntax'               => ['syntax' => 'short'],
+    //     'combine_consecutive_unsets' => true,
+    //     'general_phpdoc_annotation_remove'      => ['expectedException', 'expectedExceptionMessage', 'expectedExceptionMessageRegExp'],
+    //     'header_comment'                        => ['header' => $header],
+    //     'heredoc_to_nowdoc'                     => true,
+    //     'no_extra_consecutive_blank_lines'      => ['break', 'continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block'],
+    //     'no_unreachable_default_argument_value' => true,
+    //     'no_useless_else'                       => true,
+    //     'no_useless_return'                     => true,
+    //     'ordered_class_elements'                => true,
+    //     'ordered_imports'                       => true,
+    //     'php_unit_strict'                       => true,
+    //     'phpdoc_add_missing_param_annotation'   => true,
+    //     'phpdoc_order'                          => true,
+    //     'psr4'                                  => true,
+    //     'strict_comparison'                     => false,
+    //     'strict_param'                          => true,
+    //     'binary_operator_spaces'                => ['align_double_arrow' => true, 'align_equals' => true],
+    //     'concat_space'                          => ['spacing' => 'one'],
+    //     'no_empty_statement'                    => true,
+    //     'simplified_null_return'                => true,
+    //     'no_extra_consecutive_blank_lines'      => true,
+    //     'pre_increment'                         => false
+    // ])
     ->setFinder(
         PhpCsFixer\Finder::create()
             ->exclude('vendor')
@@ -42,4 +43,4 @@ return PhpCsFixer\Config::create()
             ->in(__DIR__)
     )
     ->setUsingCache(false)
-;
\ No newline at end of file
+;

From bfb059524c1a87bf337e8b6702bc8ae145064c3d Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Wed, 7 Mar 2018 12:02:58 +0800
Subject: [PATCH 144/643] Update .php_cs

Remove useless code
---
 .php_cs | 31 +------------------------------
 1 file changed, 1 insertion(+), 30 deletions(-)

diff --git a/.php_cs b/.php_cs
index f56582fd..eaaf7963 100644
--- a/.php_cs
+++ b/.php_cs
@@ -8,39 +8,10 @@ file that was distributed with this source code.
 EOF;
 
 return PhpCsFixer\Config::create()
-    // ->setRiskyAllowed(true)
-    // ->setRules([
-    //     '@Symfony'                   => true,
-    //     '@Symfony:risky'             => true,
-    //     'array_syntax'               => ['syntax' => 'short'],
-    //     'combine_consecutive_unsets' => true,
-    //     'general_phpdoc_annotation_remove'      => ['expectedException', 'expectedExceptionMessage', 'expectedExceptionMessageRegExp'],
-    //     'header_comment'                        => ['header' => $header],
-    //     'heredoc_to_nowdoc'                     => true,
-    //     'no_extra_consecutive_blank_lines'      => ['break', 'continue', 'extra', 'return', 'throw', 'use', 'parenthesis_brace_block', 'square_brace_block', 'curly_brace_block'],
-    //     'no_unreachable_default_argument_value' => true,
-    //     'no_useless_else'                       => true,
-    //     'no_useless_return'                     => true,
-    //     'ordered_class_elements'                => true,
-    //     'ordered_imports'                       => true,
-    //     'php_unit_strict'                       => true,
-    //     'phpdoc_add_missing_param_annotation'   => true,
-    //     'phpdoc_order'                          => true,
-    //     'psr4'                                  => true,
-    //     'strict_comparison'                     => false,
-    //     'strict_param'                          => true,
-    //     'binary_operator_spaces'                => ['align_double_arrow' => true, 'align_equals' => true],
-    //     'concat_space'                          => ['spacing' => 'one'],
-    //     'no_empty_statement'                    => true,
-    //     'simplified_null_return'                => true,
-    //     'no_extra_consecutive_blank_lines'      => true,
-    //     'pre_increment'                         => false
-    // ])
     ->setFinder(
         PhpCsFixer\Finder::create()
             ->exclude('vendor')
             ->exclude('runtime')
             ->in(__DIR__)
     )
-    ->setUsingCache(false)
-;
+    ->setUsingCache(false);

From 21c0d99fbe388da6e0c8078b5ffca89e1e0d7846 Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Wed, 7 Mar 2018 12:03:31 +0800
Subject: [PATCH 145/643] Update UserPoolConfig.php

Use single quote
---
 app/Pool/Config/UserPoolConfig.php | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php
index 628fa386..57723c20 100644
--- a/app/Pool/Config/UserPoolConfig.php
+++ b/app/Pool/Config/UserPoolConfig.php
@@ -19,7 +19,7 @@ class UserPoolConfig extends PoolProperties
      * @Value(name="${config.service.user.name}", env="${USER_POOL_NAME}")
      * @var string
      */
-    protected $name = "";
+    protected $name = '';
 
     /**
      * Minimum active number of connections
@@ -98,7 +98,7 @@ class UserPoolConfig extends PoolProperties
      * @Value(name="${config.service.user.balancer}", env="${USER_POOL_BALANCER}")
      * @var string
      */
-    protected $balancer = "";
+    protected $balancer = '';
 
     /**
      * the default provider is consul provider
@@ -106,5 +106,5 @@ class UserPoolConfig extends PoolProperties
      * @Value(name="${config.service.user.provider}", env="${USER_POOL_PROVIDER}")
      * @var string
      */
-    protected $provider = "";
-}
\ No newline at end of file
+    protected $provider = '';
+}

From 46fcf0451d87c081fc2c7458e6812ab6dee3af42 Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Wed, 7 Mar 2018 15:53:12 +0800
Subject: [PATCH 146/643] Update Dockerfile

Upgrade swoole to v2.1.1
---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index f654172d..624ea5b5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -33,7 +33,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.
     ) \
     && rm -r hiredis
     
-RUN wget https://github.com/swoole/swoole-src/archive/v2.1.0.tar.gz -O swoole.tar.gz \
+RUN wget https://github.com/swoole/swoole-src/archive/v2.1.1.tar.gz -O swoole.tar.gz \
     && mkdir -p swoole \
     && tar -xf swoole.tar.gz -C swoole --strip-components=1 \
     && rm swoole.tar.gz \

From e8233315af6634ee50ddfcd7848501bfc20b55fa Mon Sep 17 00:00:00 2001
From: Huangzhhui 
Date: Wed, 7 Mar 2018 16:02:39 +0800
Subject: [PATCH 147/643] Update README.md

Update badges
---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index eb07b5a2..de475ce3 100644
--- a/README.md
+++ b/README.md
@@ -4,13 +4,13 @@
     
 

-[![Latest Version](https://img.shields.io/badge/unstable-v0.2.6-yellow.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) +[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.0.12-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) -[![Swoft License](https://img.shields.io/badge/license-apache%202.0-lightgrey.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) # 简介 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 From a8a9888f5d7cd8ec3b7b2d650ea0d098dd91bbc1 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Wed, 7 Mar 2018 16:04:25 +0800 Subject: [PATCH 148/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de475ce3..29983ce2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.0.12-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) From cbde3965f605ac188759a2ea8c034bfecb253d74 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Wed, 7 Mar 2018 16:06:54 +0800 Subject: [PATCH 149/643] Update README.md Update badge link --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 29983ce2..2acdb627 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@

-[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) +[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) -[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://packagist.org/packages/swoft/swoft) +[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) +[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) From 38edd016c186f5d483a5e2697a5f91a380f188f8 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 7 Mar 2018 17:03:32 +0800 Subject: [PATCH 150/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2acdb627..6e023498 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ QQ 交流群: 548173319 ## Composer 安装 -* `composer create-project swoft/swoft swoft dev-master` +* `composer create-project swoft/swoft swoft` ## Docker 安装 * `docker run -p 80:80 swoft/swoft` From 6aa8361b629efab6b3c9d80d734efd132c1299a0 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 7 Mar 2018 21:42:06 +0800 Subject: [PATCH 151/643] fix typo --- app/Controllers/MiddlewareController.php | 8 ++++---- ...erSubMiddleware.php => ControllerSubMiddleware.php} | 6 +++--- ...TestMiddleware.php => ControllerTestMiddleware.php} | 10 ++-------- 3 files changed, 9 insertions(+), 15 deletions(-) rename app/Middlewares/{ControlerSubMiddleware.php => ControllerSubMiddleware.php} (90%) rename app/Middlewares/{ControlerTestMiddleware.php => ControllerTestMiddleware.php} (70%) diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php index 3feb4552..a90d232d 100644 --- a/app/Controllers/MiddlewareController.php +++ b/app/Controllers/MiddlewareController.php @@ -9,15 +9,15 @@ use App\Middlewares\GroupTestMiddleware; use App\Middlewares\ActionTestMiddleware; use App\Middlewares\SubMiddleware; -use App\Middlewares\ControlerSubMiddleware; -use App\Middlewares\ControlerTestMiddleware; +use App\Middlewares\ControllerSubMiddleware; +use App\Middlewares\ControllerTestMiddleware; /** * @Controller("middleware") - * @Middleware(class=ControlerTestMiddleware::class) + * @Middleware(class=ControllerTestMiddleware::class) * @Middlewares({ - * @Middleware(ControlerSubMiddleware::class) + * @Middleware(ControllerSubMiddleware::class) * }) */ class MiddlewareController diff --git a/app/Middlewares/ControlerSubMiddleware.php b/app/Middlewares/ControllerSubMiddleware.php similarity index 90% rename from app/Middlewares/ControlerSubMiddleware.php rename to app/Middlewares/ControllerSubMiddleware.php index a1ba52bb..8b7111ac 100644 --- a/app/Middlewares/ControlerSubMiddleware.php +++ b/app/Middlewares/ControllerSubMiddleware.php @@ -10,15 +10,15 @@ /** * the sub middleware of controler - * * @Bean() - * @uses ControlerSubMiddleware + * + * @uses ControllerSubMiddleware * @version 2017年11月29日 * @author stelin * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ -class ControlerSubMiddleware implements MiddlewareInterface +class ControllerSubMiddleware implements MiddlewareInterface { /** * @param \Psr\Http\Message\ServerRequestInterface $request diff --git a/app/Middlewares/ControlerTestMiddleware.php b/app/Middlewares/ControllerTestMiddleware.php similarity index 70% rename from app/Middlewares/ControlerTestMiddleware.php rename to app/Middlewares/ControllerTestMiddleware.php index ab621048..4d953129 100644 --- a/app/Middlewares/ControlerTestMiddleware.php +++ b/app/Middlewares/ControllerTestMiddleware.php @@ -9,16 +9,10 @@ use Swoft\Http\Message\Middleware\MiddlewareInterface; /** - * controler middleware test - * + * Controler middleware test * @Bean() - * @uses ControlerTestMiddleware - * @version 2017年11月29日 - * @author stelin - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ -class ControlerTestMiddleware implements MiddlewareInterface +class ControllerTestMiddleware implements MiddlewareInterface { /** * @param \Psr\Http\Message\ServerRequestInterface $request From 60c7175089f698826390cb6eb595aab074802810 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 7 Mar 2018 21:44:10 +0800 Subject: [PATCH 152/643] fix typo --- app/Middlewares/ControllerSubMiddleware.php | 2 +- app/Middlewares/ControllerTestMiddleware.php | 2 +- test/Cases/MiddlewareTest.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Middlewares/ControllerSubMiddleware.php b/app/Middlewares/ControllerSubMiddleware.php index 8b7111ac..6ab866dc 100644 --- a/app/Middlewares/ControllerSubMiddleware.php +++ b/app/Middlewares/ControllerSubMiddleware.php @@ -29,6 +29,6 @@ class ControllerSubMiddleware implements MiddlewareInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = $handler->handle($request); - return $response->withAddedHeader('Controler-Sub-Middleware', 'success'); + return $response->withAddedHeader('Controller-Sub-Middleware', 'success'); } } \ No newline at end of file diff --git a/app/Middlewares/ControllerTestMiddleware.php b/app/Middlewares/ControllerTestMiddleware.php index 4d953129..48b62bd9 100644 --- a/app/Middlewares/ControllerTestMiddleware.php +++ b/app/Middlewares/ControllerTestMiddleware.php @@ -23,6 +23,6 @@ class ControllerTestMiddleware implements MiddlewareInterface public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = $handler->handle($request); - return $response->withAddedHeader('Controler-Test-Middleware', 'success'); + return $response->withAddedHeader('Controller-Test-Middleware', 'success'); } } \ No newline at end of file diff --git a/test/Cases/MiddlewareTest.php b/test/Cases/MiddlewareTest.php index b5068279..6df9dc2a 100644 --- a/test/Cases/MiddlewareTest.php +++ b/test/Cases/MiddlewareTest.php @@ -41,7 +41,7 @@ public function action3() { $response = $this->request('GET', '/middleware/action3', [], parent::ACCEPT_JSON); $response->assertExactJson(['middleware3']); - $response->assertHeader('Controler-Test-Middleware', 'success'); - $response->assertHeader('Controler-Sub-Middleware', 'success'); + $response->assertHeader('Controller-Test-Middleware', 'success'); + $response->assertHeader('Controller-Sub-Middleware', 'success'); } } \ No newline at end of file From f931ae4edd868cd55a64337fcf8d9c8ed3aa8069 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Wed, 7 Mar 2018 22:03:41 +0800 Subject: [PATCH 153/643] improve redis functional test, will skip the test when no redis connection --- test/Cases/RedisControllerTest.php | 168 ++++++++++++++++++----------- 1 file changed, 106 insertions(+), 62 deletions(-) diff --git a/test/Cases/RedisControllerTest.php b/test/Cases/RedisControllerTest.php index 3cfb5e41..8c3b2b7b 100644 --- a/test/Cases/RedisControllerTest.php +++ b/test/Cases/RedisControllerTest.php @@ -2,28 +2,56 @@ namespace Swoft\Test\Cases; +use Swoft\Redis\Redis; + /** - * @uses RedisControllerTest - * @version 2017年11月30日 - * @author huangzhhui - * @copyright Copyright 2010-2017 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} + * Class RedisControllerTest + * + * @package Swoft\Test\Cases */ class RedisControllerTest extends AbstractTestCase { + protected $isRedisConnected = false; + + protected function setUp() + { + parent::setUp(); + $redis = bean(Redis::class); + try { + $redis->has('test'); + $this->isRedisConnected = true; + } catch (\Exception $e) { + // No connection or else error + } + } + + /** + * @param \Closure $closure + */ + protected function runRedisTest(\Closure $closure) + { + if ($this->isRedisConnected) { + $closure(); + } else { + $this->markTestSkipped('No redis connection'); + } + } + /** * @test * @requires extension redis */ public function cache() { - $expected = [ - true, - 'stelin', - ]; - $response = $this->request('GET', '/redis/testCache', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + 'stelin', + ]; + $response = $this->request('GET', '/redis/testCache', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -32,12 +60,14 @@ public function cache() */ public function redis() { - $expected = [ - true, - 'stelin2', - ]; - $response = $this->request('GET', '/redis/testRedis', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + 'stelin2', + ]; + $response = $this->request('GET', '/redis/testRedis', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -46,12 +76,14 @@ public function redis() */ public function func() { - $expected = [ - true, - 'stelin3', - ]; - $response = $this->request('GET', '/redis/testFunc', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + 'stelin3', + ]; + $response = $this->request('GET', '/redis/testFunc', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -60,13 +92,15 @@ public function func() */ public function func2() { - $expected = [ - true, - 'stelin3', - 'value3', - ]; - $response = $this->request('GET', '/redis/testFunc2', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + 'stelin3', + 'value3', + ]; + $response = $this->request('GET', '/redis/testFunc2', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -75,12 +109,14 @@ public function func2() */ public function delete() { - $expected = [ - true, - 1, - ]; - $response = $this->request('GET', '/redis/testDelete', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + 1, + ]; + $response = $this->request('GET', '/redis/testDelete', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -89,11 +125,13 @@ public function delete() */ public function clear() { - $expected = [ - true, - ]; - $response = $this->request('GET', '/redis/clear', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + ]; + $response = $this->request('GET', '/redis/clear', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -102,15 +140,17 @@ public function clear() */ public function multiple() { - $expected = [ - true, - [ - 'stelin6', - 'stelin8', - ], - ]; - $response = $this->request('GET', '/redis/setMultiple', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + [ + 'stelin6', + 'stelin8', + ], + ]; + $response = $this->request('GET', '/redis/setMultiple', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -119,12 +159,14 @@ public function multiple() */ public function deleteMultiple() { - $expected = [ - true, - 2, - ]; - $response = $this->request('GET', '/redis/deleteMultiple', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + 2, + ]; + $response = $this->request('GET', '/redis/deleteMultiple', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } /** @@ -133,11 +175,13 @@ public function deleteMultiple() */ public function has() { - $expected = [ - true, - true, - ]; - $response = $this->request('GET', '/redis/has', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); + $this->runRedisTest(function () { + $expected = [ + true, + true, + ]; + $response = $this->request('GET', '/redis/has', [], parent::ACCEPT_JSON); + $response->assertSuccessful()->assertJson($expected); + }); } } \ No newline at end of file From f801f97362207a65a114b1ab6c60f69179155620 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Thu, 8 Mar 2018 03:28:43 +0800 Subject: [PATCH 154/643] fixed validator unit test, cuz passing property name to validator now, and use property name in exception message --- test/Cases/ValidatorControllerTest.php | 100 ++++++++++++------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php index c771effb..37261e17 100644 --- a/test/Cases/ValidatorControllerTest.php +++ b/test/Cases/ValidatorControllerTest.php @@ -7,44 +7,44 @@ * * @uses ValidatorControllerTest * @version 2017年12月03日 - * @author stelin + * @author swoft * @copyright Copyright 2010-2016 swoft software * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class ValidatorControllerTest extends AbstractTestCase { /** - * @covers \App\Controllers\ValidatorController@string + * @covers \App\Controllers\ValidatorController::string */ public function testString() { - $response = $this->request('GET', '/validator/string/stelin', [], parent::ACCEPT_JSON); - $response->assertExactJson(['boy', 'girl', 'stelin']); + $response = $this->request('GET', '/validator/string/swoft', [], parent::ACCEPT_JSON); + $response->assertExactJson(['boy', 'girl', 'swoft']); $response = $this->request('POST', '/validator/string/c', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'c is too small (minimum is 3)']); + $response->assertExactJson(['message' => 'Parameter name length is too short (minimum is 3)']); - $response = $this->request('POST', '/validator/string/stelin', ['name' => 'a'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'a is too small (minimum is 3)']); + $response = $this->request('POST', '/validator/string/swoft', ['name' => 'a'], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'Parameter name length is too short (minimum is 3)']); - $response = $this->request('POST', '/validator/string/stelin?name=b', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'b is too small (minimum is 3)']); + $response = $this->request('POST', '/validator/string/swoft?name=b', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'Parameter name length is too short (minimum is 3)']); - $response = $this->request('POST', '/validator/string/stelin66666666', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'stelin66666666 is too big (maximum is 10)']); + $response = $this->request('POST', '/validator/string/swoft66666666', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'Parameter name length is too long (maximum is 10)']); - $response = $this->request('POST', '/validator/string/stelin', ['name' => 'stelin66666666'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'stelin66666666 is too big (maximum is 10)']); + $response = $this->request('POST', '/validator/string/swoft', ['name' => 'swoft66666666'], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'Parameter name length is too long (maximum is 10)']); - $response = $this->request('POST', '/validator/string/stelin?name=stelin66666666', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'stelin66666666 is too big (maximum is 10)']); + $response = $this->request('POST', '/validator/string/swoft?name=swoft66666666', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'Parameter name length is too long (maximum is 10)']); - $response = $this->request('POST', '/validator/string/stelinPath?name=stelinGet', ['name' => 'stelinPost'], parent::ACCEPT_JSON); - $response->assertExactJson(['stelinGet', 'stelinPost', 'stelinPath']); + $response = $this->request('POST', '/validator/string/swoftPath?name=swoftGet', ['name' => 'swoftPost'], parent::ACCEPT_JSON); + $response->assertExactJson(['swoftGet', 'swoftPost', 'swoftPath']); } /** - * @covers \App\Controllers\ValidatorController@number + * @covers \App\Controllers\ValidatorController::number */ public function testNumber() { @@ -52,70 +52,70 @@ public function testNumber() $response->assertExactJson([7, 8, 10]); $response = $this->request('POST', '/validator/number/3', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '3 is too small (minimum is 5)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/6', ['id' => '-2'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '-2 is not number']); + $response->assertExactJson(['message' => 'Parameter id is not a number']); $response = $this->request('POST', '/validator/number/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/6?id=-2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '-2 is not number']); + $response->assertExactJson(['message' => 'Parameter id is not a number']); $response = $this->request('POST', '/validator/number/6?id=2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9?id=12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); $response->assertExactJson(['9', '9', 9]); } /** - * @covers \App\Controllers\ValidatorController@float + * @covers \App\Controllers\ValidatorController::float */ public function testFloat() { $response = $this->request('GET', '/validator/float/a', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'a is not float']); + $response->assertExactJson(['message' => 'Parameter id is not float type']); $response = $this->request('GET', '/validator/float/5', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '5 is not float']); + $response->assertExactJson(['message' => 'Parameter id is not float type']); $response = $this->request('POST', '/validator/float/5.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '5 is too small (minimum is 5.1)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/float/6.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '6 is too big (maximum is 5.9)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 5)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => 5], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '5 is not float']); + $response->assertExactJson(['message' => 'Parameter id is not float type']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.0'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '5 is too small (minimum is 5.1)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '6.0'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '6 is too big (maximum is 5.9)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 5)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.2'], parent::ACCEPT_JSON); $response->assertExactJson([5.6, '5.2', 5.2]); $response = $this->request('POST', '/validator/float/5.2?id=5', [5.2], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '5 is not float']); + $response->assertExactJson(['message' => 'Parameter id is not float type']); $response = $this->request('POST', '/validator/float/5.2?id=5.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '5 is too small (minimum is 5.1)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/float/5.2?id=6.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '6 is too big (maximum is 5.9)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 5)']); $response = $this->request('POST', '/validator/float/5.2?id=5.2', ['id' => '5.2'], parent::ACCEPT_JSON); @@ -123,7 +123,7 @@ public function testFloat() } /** - * @covers \App\Controllers\ValidatorController@integer + * @covers \App\Controllers\ValidatorController::integer */ public function testInteger() { @@ -131,46 +131,46 @@ public function testInteger() $response->assertExactJson([7, 8, 10]); $response = $this->request('POST', '/validator/integer/3', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '3 is too small (minimum is 5)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/6', ['id' => 'a'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'a is not integer']); + $response->assertExactJson(['message' => 'Parameter id is not integer type']); $response = $this->request('POST', '/validator/integer/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/6?id=a', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'a is not integer']); + $response->assertExactJson(['message' => 'Parameter id is not integer type']); $response = $this->request('POST', '/validator/integer/6?id=2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '2 is too small (minimum is 5)']); + $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9?id=12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '12 is too big (maximum is 10)']); + $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); $response->assertExactJson(['9', '9', 9]); } /** - * @covers \App\Controllers\ValidatorController@enum + * @covers \App\Controllers\ValidatorController::enum */ public function testEnum() { $response = $this->request('POST', '/validator/enum/4', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '4 is not valid enum!']); + $response->assertExactJson(['message' => 'Parameter name is an invalid enum value']); $response = $this->request('POST', '/validator/enum/1?name=4', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '4 is not valid enum!']); + $response->assertExactJson(['message' => 'Parameter name is an invalid enum value']); $response = $this->request('POST', '/validator/enum/1', ['name' => '4'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => '4 is not valid enum!']); + $response->assertExactJson(['message' => 'Parameter name is an invalid enum value']); $response = $this->request('POST', '/validator/enum/1?name=a', ['name' => '3'], parent::ACCEPT_JSON); $response->assertExactJson(['a', '3', '1']); From ea62ff90f8678be149cf293630de8e96dc844d13 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Thu, 8 Mar 2018 03:29:04 +0800 Subject: [PATCH 155/643] fixed redis unit test --- app/Controllers/RedisController.php | 24 +++++++++++++----------- test/Cases/RedisControllerTest.php | 12 ++++++------ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index dc0ef1db..ee42028a 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -27,7 +27,7 @@ class RedisController public function testCache() { - $result = $this->cache->set('name', 'stelin'); + $result = $this->cache->set('name', 'swoft'); $name = $this->cache->get('name'); $this->redis->incr("count"); @@ -39,7 +39,7 @@ public function testCache() public function testRedis() { - $result = $this->redis->set('nameRedis', 'stelin2'); + $result = $this->redis->set('nameRedis', 'swoft2'); $name = $this->redis->get('nameRedis'); return [$result, $name]; @@ -47,7 +47,7 @@ public function testRedis() public function testFunc() { - $result = cache()->set('nameFunc', 'stelin3'); + $result = cache()->set('nameFunc', 'swoft3'); $name = cache()->get('nameFunc'); return [$result, $name]; @@ -55,7 +55,7 @@ public function testFunc() public function testFunc2() { - $result = cache()->set('nameFunc2', 'stelin3'); + $result = cache()->set('nameFunc2', 'swoft3'); $name = cache('nameFunc2'); $name2 = cache('nameFunc3', 'value3'); @@ -64,7 +64,7 @@ public function testFunc2() public function testDelete() { - $result = $this->cache->set('name', 'stelin'); + $result = $this->cache->set('name', 'swoft'); $del = $this->cache->delete('name'); return [$result, $del]; @@ -79,7 +79,7 @@ public function clear() public function setMultiple() { - $result = $this->cache->setMultiple(['name6' => 'stelin6', 'name8' => 'stelin8']); + $result = $this->cache->setMultiple(['name6' => 'swoft6', 'name8' => 'swoft8']); $ary = $this->cache->getMultiple(['name6', 'name8']); return [$result, $ary]; @@ -87,7 +87,7 @@ public function setMultiple() public function deleteMultiple() { - $result = $this->cache->setMultiple(['name6' => 'stelin6', 'name8' => 'stelin8']); + $result = $this->cache->setMultiple(['name6' => 'swoft6', 'name8' => 'swoft8']); $ary = $this->cache->deleteMultiple(['name6', 'name8']); return [$result, $ary]; @@ -95,7 +95,7 @@ public function deleteMultiple() public function has() { - $result = $this->cache->set("name666", 'stelin666'); + $result = $this->cache->set("name666", 'swoft666'); $ret = $this->cache->has('name666'); return [$result, $ret]; @@ -103,13 +103,15 @@ public function has() public function testDefer() { - $ret1 = $this->redis->deferCall('set', ['name1', 'stelin1']); - $ret2 = $this->redis->deferCall('set', ['name2', 'stelin2']); + $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); + $ret2 = $this->redis->deferCall('set', ['name2', 'swoft2']); $r1 = $ret1->getResult(); + $r2 = 1; $r2 = $ret2->getResult(); - $ary = $this->redis->getMultiple(['name1', 'name2']); + $ary = 1; + // $ary = $this->redis->getMultiple(['name1', 'name2']); return [$r1, $r2, $ary]; } diff --git a/test/Cases/RedisControllerTest.php b/test/Cases/RedisControllerTest.php index 8c3b2b7b..27f17759 100644 --- a/test/Cases/RedisControllerTest.php +++ b/test/Cases/RedisControllerTest.php @@ -47,7 +47,7 @@ public function cache() $this->runRedisTest(function () { $expected = [ true, - 'stelin', + 'swoft', ]; $response = $this->request('GET', '/redis/testCache', [], parent::ACCEPT_JSON); $response->assertSuccessful()->assertJson($expected); @@ -63,7 +63,7 @@ public function redis() $this->runRedisTest(function () { $expected = [ true, - 'stelin2', + 'swoft2', ]; $response = $this->request('GET', '/redis/testRedis', [], parent::ACCEPT_JSON); $response->assertSuccessful()->assertJson($expected); @@ -79,7 +79,7 @@ public function func() $this->runRedisTest(function () { $expected = [ true, - 'stelin3', + 'swoft3', ]; $response = $this->request('GET', '/redis/testFunc', [], parent::ACCEPT_JSON); $response->assertSuccessful()->assertJson($expected); @@ -95,7 +95,7 @@ public function func2() $this->runRedisTest(function () { $expected = [ true, - 'stelin3', + 'swoft3', 'value3', ]; $response = $this->request('GET', '/redis/testFunc2', [], parent::ACCEPT_JSON); @@ -144,8 +144,8 @@ public function multiple() $expected = [ true, [ - 'stelin6', - 'stelin8', + 'name6' => 'swoft6', + 'name8' => 'swoft8', ], ]; $response = $this->request('GET', '/redis/setMultiple', [], parent::ACCEPT_JSON); From 251f77703f2af7554f124ea5bc93f45fc9cdeb36 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Fri, 9 Mar 2018 11:00:59 +0800 Subject: [PATCH 156/643] add http2 --- .env.example | 3 +++ config/server.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.env.example b/.env.example index 62291e45..478c4927 100644 --- a/.env.example +++ b/.env.example @@ -19,6 +19,9 @@ TCP_MODE=SWOOLE_PROCESS TCP_TYPE=SWOOLE_SOCK_TCP TCP_PACKAGE_MAX_LENGTH=2048 TCP_OPEN_EOF_CHECK=false +OPEN_HTTP2_PROTOCOL=true +SSL_CERT_FILE=/path/to/ssl_cert_file +SSL_KEY_FILE=/path/to/ssl_key_file # Crontab CRONTAB_TASK_COUNT=1024 diff --git a/config/server.php b/config/server.php index 9cd4fe82..fab1e76f 100644 --- a/config/server.php +++ b/config/server.php @@ -36,5 +36,8 @@ 'upload_tmp_dir' => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'), 'document_root' => env('DOCUMENT_ROOT', BASE_PATH . '/public'), 'enable_static_handler' => env('ENABLE_STATIC_HANDLER', true), + 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), + 'ssl_cert_file' => env('SSL_CERT_FILE', ''), + 'ssl_key_file' => env('SSL_KEY_FILE', ''), ], ]; \ No newline at end of file From c6c507369b3500f08d3e2658f8697523d12688d6 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Fri, 9 Mar 2018 12:41:48 +0800 Subject: [PATCH 157/643] Update Dockerfile add apt-get autoremove --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 624ea5b5..b67ba368 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,8 @@ RUN apt-get update \ zip \ libz-dev \ libssl-dev \ - && apt-get clean + && apt-get clean \ + && apt-get autoremove RUN curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ From aef478371e20208705d6a552e0933b489c6bfc2a Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Fri, 9 Mar 2018 16:25:25 +0800 Subject: [PATCH 158/643] add http2 --- .env.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index 478c4927..9bdaf103 100644 --- a/.env.example +++ b/.env.example @@ -19,9 +19,6 @@ TCP_MODE=SWOOLE_PROCESS TCP_TYPE=SWOOLE_SOCK_TCP TCP_PACKAGE_MAX_LENGTH=2048 TCP_OPEN_EOF_CHECK=false -OPEN_HTTP2_PROTOCOL=true -SSL_CERT_FILE=/path/to/ssl_cert_file -SSL_KEY_FILE=/path/to/ssl_key_file # Crontab CRONTAB_TASK_COUNT=1024 @@ -35,6 +32,9 @@ DISPATCH_MODE=2 LOG_FILE=@runtime/logs/swoole.log TASK_WORKER_NUM=1 PACKAGE_MAX_LENGTH=2048 +OPEN_HTTP2_PROTOCOL=false +SSL_CERT_FILE=/path/to/ssl_cert_file +SSL_KEY_FILE=/path/to/ssl_key_file # Database Master nodes DB_NAME=dbMaster From 17aa6c71c1e1a8e9b89dd6514fc24fffffd324b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 9 Mar 2018 19:35:36 +0800 Subject: [PATCH 159/643] add Runtime environment question --- .github/ISSUE_TEMPLATE.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 0cce5afa..be97a863 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,10 +1,11 @@ -| Q | A -| ---------------- | ----- -| Bug report? | yes/no -| Feature request? | yes/no -| Swoft version | x.y.z -| Swoole version | x.y.z (by `php --ri swoole`) -| PHP version | x.y.z (by `php -v`) +| Q | A +| ------------------- | ----- +| Bug report? | yes/no +| Feature request? | yes/no +| Swoft version | x.y.z +| Swoole version | x.y.z (by `php --ri swoole`) +| PHP version | x.y.z (by `php -v`) +| Runtime environment | Win10/Mac/CentOS 7/Ubuntu/Docker etc. **Details** From 0bfc87697bcf095378262c2a3c95481bdc1f4e6a Mon Sep 17 00:00:00 2001 From: kcloze <460309735@qq.com> Date: Fri, 9 Mar 2018 22:47:43 +0800 Subject: [PATCH 160/643] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=AE=B9=E5=99=A8=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index c44f15f0..bbbaea52 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ version: '2' services: swoft: + container_name: swoft image: swoft/swoft:latest ports: - "80:80" From 55a567471341a7b27de7076e963d5df6a2987530 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Sat, 10 Mar 2018 03:35:29 +0800 Subject: [PATCH 161/643] Update default configuration --- .env.example | 4 ++-- config/beans/base.php | 2 +- config/beans/log.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 62291e45..c4713f06 100644 --- a/.env.example +++ b/.env.example @@ -24,9 +24,9 @@ TCP_OPEN_EOF_CHECK=false CRONTAB_TASK_COUNT=1024 CRONTAB_TASK_QUEUE=2048 -# Settings +# Swoole Settings WORKER_NUM=1 -MAX_REQUEST=10000 +MAX_REQUEST=100000 DAEMONIZE=0 DISPATCH_MODE=2 LOG_FILE=@runtime/logs/swoole.log diff --git a/config/beans/base.php b/config/beans/base.php index d4110c58..4e277e3b 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -4,7 +4,7 @@ 'serverDispatcher' => [ 'middlewares' => [ \Swoft\View\Middleware\ViewMiddleware::class, - \Swoft\Session\Middleware\SessionMiddleware::class, + //\Swoft\Session\Middleware\SessionMiddleware::class, ] ], 'httpRouter' => [ diff --git a/config/beans/log.php b/config/beans/log.php index d871d96f..6385f0a9 100644 --- a/config/beans/log.php +++ b/config/beans/log.php @@ -22,8 +22,8 @@ ], 'logger' => [ 'name' => APP_NAME, - 'flushInterval' => 100, - 'flushRequest' => true, + 'flushInterval' => 100000, + 'flushRequest' => false, 'handlers' => [ '${noticeHandler}', '${applicationHandler}', From 8c5cad409e60a8527177879514ec9e16cafd0b11 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Sat, 10 Mar 2018 03:42:50 +0800 Subject: [PATCH 162/643] Update README.md Fix badge link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e023498..e9d5221e 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ [![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) -[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) From ce0cbd6aafd6175de6f756af2feb346f17e72d83 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sat, 10 Mar 2018 22:25:04 +0800 Subject: [PATCH 163/643] modify demo --- app/Exception/SwoftExceptionHandler.php | 2 ++ app/Tasks/SyncTask.php | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index 31cb08fd..6001e4f9 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -2,6 +2,7 @@ namespace App\Exception; +use Swoft\App; use Swoft\Bean\Annotation\ExceptionHandler; use Swoft\Bean\Annotation\Handler; use Swoft\Exception\RuntimeException; @@ -40,6 +41,7 @@ public function handlerException(Response $response, \Throwable $throwable) $exception = $throwable->getMessage(); $data = ['msg' => $exception, 'file' => $file, 'line' => $line, 'code' => $code]; + App::error(json_encode($data)); return $response->json($data); } diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index f9ca1554..f271e5ac 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -87,9 +87,9 @@ public function cache() * @return array */ public function mysql(){ - $result = User::findById(425)->getResult(); + $result = User::findById(720)->getResult(); - $query = User::findById(426); + $query = User::findById(720); /* @var User $user */ $user = $query->getResult(User::class); From dcffb81aa8bf564918262ec11c6b9a9410b9fb38 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Sun, 11 Mar 2018 19:00:48 +0800 Subject: [PATCH 164/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9d5221e..f0c467f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

    - swoft + swoft

From 8a295e761ecb5ff5be0fb6d21c1aeda11dcfbd01 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Sun, 11 Mar 2018 19:12:29 +0800 Subject: [PATCH 165/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0c467f5..e58220ea 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

    - swoft + swoft

From 7550b8482758ca39877ab3f1b077a2345f5a7eba Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 11 Mar 2018 22:21:42 +0800 Subject: [PATCH 166/643] modify demo --- .env.example | 4 +- app/Controllers/OrmController.php | 81 ++++++++++++++++++++--------- app/Controllers/RedisController.php | 9 ++++ 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/.env.example b/.env.example index 3451cc46..689e2f1f 100644 --- a/.env.example +++ b/.env.example @@ -44,9 +44,7 @@ DB_MAX_ACTIVE=10 DB_MAX_WAIT=20 DB_MAX_WAIT_TIME=3 DB_MAX_IDLE_TIME=60 -DB_MAX_WAIT_TIME=3 -DB_MAX_IDLE_TIME=60 -DB_TIMEOUT=200 +DB_TIMEOUT=2 # Database Slave nodes DB_SLAVE_NAME=dbSlave diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 6678a94c..2019d2e6 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -4,6 +4,7 @@ use App\Models\Entity\Count; use App\Models\Entity\User; +use Swoft\Db\Pool; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Db\EntityManager; use Swoft\Db\QueryBuilder; @@ -209,9 +210,9 @@ public function find() */ public function arFindId() { - $result = User::findById(720)->getResult(); + $result = User::findById(4212)->getResult(); - $query = User::findById(720); + $query = User::findById(4212); /* @var User $user */ $user = $query->getResult(User::class); @@ -345,32 +346,62 @@ public function arCon() return [$result1, $result2]; } - public function sql() { - $params = [ - ['uid', 433], - ['uid2', 434], - ['uid3', 431, Types::INT], - ]; - $em = EntityManager::create(); -// $querySql = "SELECT * FROM user AS u LEFT JOIN count AS c ON u.id=c.uid WHERE u.id IN (:uid, :uid1, :uid3) ORDER BY u.id DESC LIMIT 2"; -// $query = $em->createQuery($querySql); -// $query->setParameter('uid', 433); -// $query->setParameter('uid2', 434); -// $query->setParameter('uid3', 431); -// $query->setParameters($params); - - $querySql = 'SELECT * FROM user AS u LEFT JOIN count AS c ON u.id=c.uid WHERE u.id IN (?, ?, ?) ORDER BY u.id DESC LIMIT 2'; - $query = $em->createQuery($querySql); - $query->setParameter(1, 433); - $query->setParameter(2, 434); - $query->setParameter(3, 431); - - $result = $query->execute(); - $sql = $query->getSql(); + $ids = [4212, 4213]; + $poolId = Pool::MASTER; + + $em = EntityManager::create($poolId); + $result = $em->createQuery('select * from user where id in(?, ?) and name = ? order by id desc limit 2') + ->setParameter(0, $ids[0]) + ->setParameter(1, $ids[1]) + ->setParameter(2, 'stelin') + ->execute()->getResult(); $em->close(); - return [$result, $sql]; + $em = EntityManager::create($poolId); + $result2 = $em->createQuery('select * from user where id in(?, ?) and name = ? order by id desc limit 2') + ->setParameter(0, $ids[0]) + ->setParameter(1, $ids[1]) + ->setParameter(2, 'stelin', Types::STRING) + ->execute()->getResult(); + $em->close(); + + $em = EntityManager::create($poolId); + $result3 = $em->createQuery('select * from user where id in(?, ?) and name = ? order by id desc limit 2') + ->setParameters([$ids[0], $ids[1], 'stelin']) + ->execute()->getResult(); + $em->close(); + + $em = EntityManager::create($poolId); + $result4 = $em->createQuery('select * from user where id in(:id1, :id2) and name = :name order by id desc limit 2') + ->setParameter(':id1', $ids[0]) + ->setParameter('id2', $ids[1]) + ->setParameter('name', 'stelin') + ->execute()->getResult(); + $em->close(); + + $em = EntityManager::create($poolId); + $result5 = $em->createQuery('select * from user where id in(:id1, :id2) and name = :name order by id desc limit 2') + ->setParameters([ + 'id1' => $ids[0], + ':id2' => $ids[1], + 'name' => 'stelin' + ]) + ->execute()->getResult(); + $em->close(); + + + $em = EntityManager::create($poolId); + $result6 = $em->createQuery('select * from user where id in(:id1, :id2) and name = :name order by id desc limit 2') + ->setParameters([ + ['id1', $ids[0]], + [':id2', $ids[1], Types::INT], + ['name', 'stelin', Types::STRING], + ]) + ->execute()->getResult(); + $em->close(); + + return [\count($result)]; } } \ No newline at end of file diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index ee42028a..8dbca636 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -3,6 +3,7 @@ namespace App\Controllers; +use App\Models\Entity\User; use Swoft\Bean\Annotation\Inject; use Swoft\Cache\Cache; use Swoft\Http\Server\Bean\Annotation\Controller; @@ -45,6 +46,14 @@ public function testRedis() return [$result, $name]; } + public function ab() + { + $result1 = User::query()->select('*')->where('id', '720')->limit(1)->execute()->getResult(); + $result2 = $this->redis->set('test1', 1); + + return [$result1, $result2]; + } + public function testFunc() { $result = cache()->set('nameFunc', 'swoft3'); From 8d6bc50387a620873410edd4b81cf3e1ac10e01d Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Mon, 12 Mar 2018 00:58:15 +0800 Subject: [PATCH 167/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e58220ea..4cf3d59b 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) # 简介 -首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 +首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 - 基于 Swoole 扩展 - 内置协程网络服务器 From 116697ee46b3eecefef29a6c43fcce508a0e38d6 Mon Sep 17 00:00:00 2001 From: lixiaopei Date: Mon, 12 Mar 2018 15:36:45 +0800 Subject: [PATCH 168/643] =?UTF-8?q?=E5=A4=9A=E6=A8=A1=E5=9D=97=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E7=A4=BA=E8=8C=83=EF=BC=8C=E8=A7=84=E8=8C=83php-cs-fi?= =?UTF-8?q?xer=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .php_cs | 6 + app/Controllers/Admin/DemoController.php | 190 +++++++++++++++++++++++ app/Controllers/Api/RestController.php | 113 ++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 app/Controllers/Admin/DemoController.php create mode 100644 app/Controllers/Api/RestController.php diff --git a/.php_cs b/.php_cs index eaaf7963..10ebe9ed 100644 --- a/.php_cs +++ b/.php_cs @@ -8,6 +8,12 @@ file that was distributed with this source code. EOF; return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setRules([ + 'header_comment' => ['header' => $header], + + ]) + ->setFinder( PhpCsFixer\Finder::create() ->exclude('vendor') diff --git a/app/Controllers/Admin/DemoController.php b/app/Controllers/Admin/DemoController.php new file mode 100644 index 00000000..78ce8716 --- /dev/null +++ b/app/Controllers/Admin/DemoController.php @@ -0,0 +1,190 @@ +query(); + // 获取name参数默认值defaultName + $getName = $request->query('name', 'defaultName'); + // 获取所有POST参数 + $post = $request->post(); + // 获取name参数默认值defaultName + $postName = $request->post('name', 'defaultName'); + // 获取所有参,包括GET或POST + $inputs = $request->input(); + // 获取name参数默认值defaultName + $inputName = $request->input('name', 'defaultName'); + + return compact('get', 'getName', 'post', 'postName', 'inputs', 'inputName'); + } + + /** + * 定义一个route,支持get,以"/"开头的定义,直接是根路径,处理uri=/index2. + * + * @RequestMapping(route="/index2", method=RequestMethod::GET) + */ + public function index2() + { + Coroutine::create(function () { + App::trace('this is child trace' . Coroutine::id()); + Coroutine::create(function () { + App::trace('this is child child trace' . Coroutine::id()); + }); + }); + + return 'success'; + } + + /** + * 没有使用注解,自动解析注入,默认支持get和post. + */ + public function task() + { + $result = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_CO); + $mysql = Task::deliver('test', 'testMysql', [], Task::TYPE_CO); + $http = Task::deliver('test', 'testHttp', [], Task::TYPE_CO, 20); + $rpc = Task::deliver('test', 'testRpc', [], Task::TYPE_CO, 5); + $result1 = Task::deliver('test', 'asyncTask', [], Task::TYPE_ASYNC); + + return [$rpc, $http, $mysql, $result, $result1]; + } + + public function index6() + { + throw new Exception('AAAA'); + // $a = $b; + $A = new AAA(); + + return ['data6']; + } + + /** + * 子协程测试. + */ + public function cor() + { + // 创建子协程 + Coroutine::create(function () { + App::error('child cor error msg'); + App::trace('child cor error msg'); + }); + + // 当前协程id + $cid = Coroutine::id(); + + // 当前运行上下文ID, 协程环境中,顶层协程ID; 任务中,当前任务taskid; 自定义进程中,当前进程ID(pid) + $tid = Coroutine::tid(); + + return [$cid, $tid]; + } + + /** + * 国际化测试. + */ + public function i18n() + { + $data[] = translate('title', [], 'zh'); + $data[] = translate('title', [], 'en'); + $data[] = translate('msg.body', ['stelin', 999], 'en'); + $data[] = translate('msg.body', ['stelin', 666], 'en'); + + return $data; + } + + /** + * 视图渲染demo - 没有使用布局文件. + * + * @RequestMapping() + * @View(template="demo/view") + */ + public function view() + { + $data = [ + 'name' => 'Swoft', + 'repo' => 'https://github.com/swoft-cloud/swoft', + 'doc' => 'https://doc.swoft.org/', + 'doc1' => 'https://swoft-cloud.github.io/swoft-doc/', + 'method' => __METHOD__, + ]; + + return $data; + } + + /** + * 视图渲染demo - 使用布局文件. + * + * @RequestMapping() + * @View(template="demo/content", layout="layouts/default.php") + */ + public function layout() + { + $layout = 'layouts/default.php'; + $data = [ + 'name' => 'Swoft', + 'repo' => 'https://github.com/swoft-cloud/swoft', + 'doc' => 'https://doc.swoft.org/', + 'doc1' => 'https://swoft-cloud.github.io/swoft-doc/', + 'method' => __METHOD__, + 'layoutFile' => $layout, + ]; + + return $data; + } +} diff --git a/app/Controllers/Api/RestController.php b/app/Controllers/Api/RestController.php new file mode 100644 index 00000000..55ab7830 --- /dev/null +++ b/app/Controllers/Api/RestController.php @@ -0,0 +1,113 @@ +input('name'); + + $bodyParams = $request->getBodyParams(); + $bodyParams = empty($bodyParams) ? ['create', $name] : $bodyParams; + + return $bodyParams; + } + + /** + * 查询一个用户信息 + * 地址:/api/user/6. + * + * @RequestMapping(route="{uid}", method={RequestMethod::GET}) + * + * @param int $uid + * + * @return array + */ + public function getUser(int $uid) + { + return ['getUser', $uid]; + } + + /** + * 查询用户的书籍信息 + * 地址:/api/user/6/book/8. + * + * @RequestMapping(route="{userId}/book/{bookId}", method={RequestMethod::GET}) + * + * @param int $userId + * @param string $bookId + * + * @return array + */ + public function getBookFromUser(int $userId, string $bookId) + { + return ['bookFromUser', $userId, $bookId]; + } + + /** + * 删除一个用户信息 + * 地址:/api/user/6. + * + * @RequestMapping(route="{uid}", method={RequestMethod::DELETE}) + * + * @param int $uid + * + * @return array + */ + public function deleteUser(int $uid) + { + return ['delete', $uid]; + } + + /** + * 更新一个用户信息 + * 地址:/api/user/6. + * + * @RequestMapping(route="{uid}", method={RequestMethod::PUT, RequestMethod::PATCH}) + * + * @param int $uid + * @param Request $request + * + * @return array + */ + public function updateUser(Request $request, int $uid) + { + $body = $request->getBodyParams(); + $body['update'] = 'update'; + $body['uid'] = $uid; + + return $body; + } +} From 7294b6cab72e7516eb1f0e3fd39198be65c5e2bc Mon Sep 17 00:00:00 2001 From: lixiaopei Date: Mon, 12 Mar 2018 15:44:11 +0800 Subject: [PATCH 169/643] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .php_cs | 1 + app/Boot/MyProcess.php | 7 +++++++ app/Breaker/UserBreaker.php | 7 +++++++ app/Commands/TestCommand.php | 7 +++++++ app/Controllers/Admin/DemoController.php | 7 +++++++ app/Controllers/Api/RestController.php | 7 +++++++ app/Controllers/DemoController.php | 7 +++++++ app/Controllers/ExceptionController.php | 7 +++++++ app/Controllers/HttpClientController.php | 7 +++++++ app/Controllers/IndexController.php | 7 +++++++ app/Controllers/MiddlewareController.php | 7 +++++++ app/Controllers/OrmController.php | 7 +++++++ app/Controllers/Psr7Controller.php | 7 +++++++ app/Controllers/RedisController.php | 7 +++++++ app/Controllers/RestController.php | 7 +++++++ app/Controllers/RouteController.php | 7 +++++++ app/Controllers/RpcController.php | 7 +++++++ app/Controllers/SessionController.php | 7 +++++++ app/Controllers/TaskController.php | 7 +++++++ app/Controllers/ValidatorController.php | 7 +++++++ app/Exception/SwoftExceptionHandler.php | 7 +++++++ app/Fallback/DemoServiceFallback.php | 7 +++++++ app/Lib/DemoInterface.php | 7 +++++++ app/Lib/MdDemoInterface.php | 7 +++++++ app/Listener/TaskFinish.php | 7 +++++++ app/Middlewares/ActionTestMiddleware.php | 7 +++++++ app/Middlewares/ControllerSubMiddleware.php | 7 +++++++ app/Middlewares/ControllerTestMiddleware.php | 7 +++++++ app/Middlewares/GroupTestMiddleware.php | 7 +++++++ app/Middlewares/ServiceMiddleware.php | 7 +++++++ app/Middlewares/ServiceSubMiddleware.php | 7 +++++++ app/Middlewares/SubMiddleware.php | 7 +++++++ app/Middlewares/SubMiddlewares.php | 7 +++++++ app/Models/Dao/UserDao.php | 7 +++++++ app/Models/Dao/UserExtDao.php | 7 +++++++ app/Models/Data/UserData.php | 7 +++++++ app/Models/Data/UserExtData.php | 7 +++++++ app/Models/Entity/Count.php | 7 +++++++ app/Models/Entity/User.php | 7 +++++++ app/Models/Logic/IndexLogic.php | 7 +++++++ app/Models/Logic/UserLogic.php | 7 +++++++ app/Pool/Config/UserPoolConfig.php | 7 +++++++ app/Pool/UserServicePool.php | 7 +++++++ app/Process/MyProcess.php | 7 +++++++ app/Services/DemoService.php | 7 +++++++ app/Services/DemoServiceV2.php | 7 +++++++ app/Services/MiddlewareService.php | 7 +++++++ app/Swoft.php | 9 ++++++--- app/Tasks/SyncTask.php | 7 +++++++ bin/bootstrap.php | 8 ++++++++ config/beans/base.php | 7 +++++++ config/beans/console.php | 8 ++++++++ config/beans/log.php | 8 ++++++++ config/beans/service.php | 8 ++++++++ config/define.php | 8 +++++++- config/properties/app.php | 8 ++++++++ config/properties/breaker.php | 8 ++++++++ config/properties/cache.php | 8 ++++++++ config/properties/db.php | 8 ++++++++ config/properties/provider.php | 8 ++++++++ config/properties/service.php | 8 ++++++++ config/server.php | 8 ++++++++ resources/languages/en/default.php | 8 ++++++++ resources/languages/en/msg.php | 8 ++++++++ resources/languages/zh/default.php | 8 ++++++++ resources/languages/zh/msg.php | 8 ++++++++ test/Cases/AbstractTestCase.php | 7 +++++++ test/Cases/DemoControllerTest.php | 7 +++++++ test/Cases/IndexControllerTest.php | 7 +++++++ test/Cases/MiddlewareTest.php | 7 +++++++ test/Cases/RedisControllerTest.php | 7 +++++++ test/Cases/RestTest.php | 7 +++++++ test/Cases/RouteTest.php | 7 +++++++ test/Cases/ValidatorControllerTest.php | 7 +++++++ test/bootstrap.php | 8 ++++++++ 75 files changed, 534 insertions(+), 4 deletions(-) diff --git a/.php_cs b/.php_cs index 10ebe9ed..bb32d9d4 100644 --- a/.php_cs +++ b/.php_cs @@ -11,6 +11,7 @@ return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setRules([ 'header_comment' => ['header' => $header], + 'array_syntax' => ['syntax' => 'short'], ]) diff --git a/app/Boot/MyProcess.php b/app/Boot/MyProcess.php index 6398c328..7391565f 100644 --- a/app/Boot/MyProcess.php +++ b/app/Boot/MyProcess.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Boot; use Swoft\App; diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php index 00830b9e..9ff2114e 100644 --- a/app/Breaker/UserBreaker.php +++ b/app/Breaker/UserBreaker.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Breaker; use Swoft\Sg\Bean\Annotation\Breaker; diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php index 3e18ea27..9b39dc24 100644 --- a/app/Commands/TestCommand.php +++ b/app/Commands/TestCommand.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Commands; use App\Models\Logic\UserLogic; diff --git a/app/Controllers/Admin/DemoController.php b/app/Controllers/Admin/DemoController.php index 78ce8716..6680d243 100644 --- a/app/Controllers/Admin/DemoController.php +++ b/app/Controllers/Admin/DemoController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers\Admin; use App\Models\Logic\IndexLogic; diff --git a/app/Controllers/Api/RestController.php b/app/Controllers/Api/RestController.php index 55ab7830..5ae8bac0 100644 --- a/app/Controllers/Api/RestController.php +++ b/app/Controllers/Api/RestController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers\Api; use Swoft\Http\Message\Server\Request; diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index aa86adc0..7af84059 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use App\Models\Logic\IndexLogic; diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php index 263bdcbf..3cb05d00 100644 --- a/app/Controllers/ExceptionController.php +++ b/app/Controllers/ExceptionController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Exception\BadMethodCallException; diff --git a/app/Controllers/HttpClientController.php b/app/Controllers/HttpClientController.php index 0c7fcc17..07c3df16 100644 --- a/app/Controllers/HttpClientController.php +++ b/app/Controllers/HttpClientController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\HttpClient\Client; diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index f3e404d0..880b3683 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Server\Bean\Annotation\Controller; diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php index a90d232d..64f09f69 100644 --- a/app/Controllers/MiddlewareController.php +++ b/app/Controllers/MiddlewareController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Server\Bean\Annotation\Controller; diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 6678a94c..82ac7e7e 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use App\Models\Entity\Count; diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index 121fb4ff..48fa082d 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Psr\Http\Message\UploadedFileInterface; diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index ee42028a..482f08fe 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php index 3f9a5fc8..e039165b 100644 --- a/app/Controllers/RestController.php +++ b/app/Controllers/RestController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Server\Bean\Annotation\Controller; diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 5ff7f1da..4b47edaf 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Server\Bean\Annotation\Controller; diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index 7bee9f4e..19ce5dcc 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use App\Lib\DemoInterface; diff --git a/app/Controllers/SessionController.php b/app/Controllers/SessionController.php index 64d0ebfc..e3c8613f 100644 --- a/app/Controllers/SessionController.php +++ b/app/Controllers/SessionController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Message\Server\Request; diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php index ba69ef06..2c27eab8 100644 --- a/app/Controllers/TaskController.php +++ b/app/Controllers/TaskController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Server\Bean\Annotation\Controller; diff --git a/app/Controllers/ValidatorController.php b/app/Controllers/ValidatorController.php index 8c52bb50..a9dc29ae 100644 --- a/app/Controllers/ValidatorController.php +++ b/app/Controllers/ValidatorController.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Controllers; use Swoft\Http\Server\Bean\Annotation\Controller; diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index 31cb08fd..855d8611 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Exception; use Swoft\Bean\Annotation\ExceptionHandler; diff --git a/app/Fallback/DemoServiceFallback.php b/app/Fallback/DemoServiceFallback.php index 5ba02d3e..fa9db9fe 100644 --- a/app/Fallback/DemoServiceFallback.php +++ b/app/Fallback/DemoServiceFallback.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Fallback; use App\Lib\DemoInterface; diff --git a/app/Lib/DemoInterface.php b/app/Lib/DemoInterface.php index 1c7bcaf9..53fc3139 100644 --- a/app/Lib/DemoInterface.php +++ b/app/Lib/DemoInterface.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Lib; use Swoft\Core\ResultInterface; diff --git a/app/Lib/MdDemoInterface.php b/app/Lib/MdDemoInterface.php index 59a6d69a..81288c55 100644 --- a/app/Lib/MdDemoInterface.php +++ b/app/Lib/MdDemoInterface.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Lib; /** diff --git a/app/Listener/TaskFinish.php b/app/Listener/TaskFinish.php index dc57e6ad..8f71d9c1 100644 --- a/app/Listener/TaskFinish.php +++ b/app/Listener/TaskFinish.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Listener; use Swoft\Bean\Annotation\Listener; diff --git a/app/Middlewares/ActionTestMiddleware.php b/app/Middlewares/ActionTestMiddleware.php index 07c8f50d..780753d4 100644 --- a/app/Middlewares/ActionTestMiddleware.php +++ b/app/Middlewares/ActionTestMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/ControllerSubMiddleware.php b/app/Middlewares/ControllerSubMiddleware.php index 6ab866dc..382ea7b8 100644 --- a/app/Middlewares/ControllerSubMiddleware.php +++ b/app/Middlewares/ControllerSubMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/ControllerTestMiddleware.php b/app/Middlewares/ControllerTestMiddleware.php index 48b62bd9..10debae1 100644 --- a/app/Middlewares/ControllerTestMiddleware.php +++ b/app/Middlewares/ControllerTestMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/GroupTestMiddleware.php b/app/Middlewares/GroupTestMiddleware.php index 0304134a..2a080897 100644 --- a/app/Middlewares/GroupTestMiddleware.php +++ b/app/Middlewares/GroupTestMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/ServiceMiddleware.php b/app/Middlewares/ServiceMiddleware.php index ef748393..0ede6794 100644 --- a/app/Middlewares/ServiceMiddleware.php +++ b/app/Middlewares/ServiceMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/ServiceSubMiddleware.php b/app/Middlewares/ServiceSubMiddleware.php index 12c8c347..8b4c199b 100644 --- a/app/Middlewares/ServiceSubMiddleware.php +++ b/app/Middlewares/ServiceSubMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/SubMiddleware.php b/app/Middlewares/SubMiddleware.php index 43bf0d4d..a4259bb5 100644 --- a/app/Middlewares/SubMiddleware.php +++ b/app/Middlewares/SubMiddleware.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Middlewares/SubMiddlewares.php b/app/Middlewares/SubMiddlewares.php index ecd9b42e..99495b82 100644 --- a/app/Middlewares/SubMiddlewares.php +++ b/app/Middlewares/SubMiddlewares.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Middlewares; use Psr\Http\Server\RequestHandlerInterface; diff --git a/app/Models/Dao/UserDao.php b/app/Models/Dao/UserDao.php index 54d75fd0..950d227c 100644 --- a/app/Models/Dao/UserDao.php +++ b/app/Models/Dao/UserDao.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Dao; use Swoft\Bean\Annotation\Bean; diff --git a/app/Models/Dao/UserExtDao.php b/app/Models/Dao/UserExtDao.php index ae1cda44..e79027b0 100644 --- a/app/Models/Dao/UserExtDao.php +++ b/app/Models/Dao/UserExtDao.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Dao; use Swoft\Bean\Annotation\Bean; diff --git a/app/Models/Data/UserData.php b/app/Models/Data/UserData.php index f6c6c256..55c854f3 100644 --- a/app/Models/Data/UserData.php +++ b/app/Models/Data/UserData.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Data; use App\Models\Dao\UserDao; diff --git a/app/Models/Data/UserExtData.php b/app/Models/Data/UserExtData.php index eb7e1840..fb0e5e0b 100644 --- a/app/Models/Data/UserExtData.php +++ b/app/Models/Data/UserExtData.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Data; use App\Models\Dao\UserExtDao; diff --git a/app/Models/Entity/Count.php b/app/Models/Entity/Count.php index f91bac52..5ab7066f 100644 --- a/app/Models/Entity/Count.php +++ b/app/Models/Entity/Count.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Entity; use Swoft\Db\Bean\Annotation\Column; diff --git a/app/Models/Entity/User.php b/app/Models/Entity/User.php index 65695418..5bb238d7 100644 --- a/app/Models/Entity/User.php +++ b/app/Models/Entity/User.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Entity; use Swoft\Db\Bean\Annotation\Id; diff --git a/app/Models/Logic/IndexLogic.php b/app/Models/Logic/IndexLogic.php index 27a852eb..45d2a482 100644 --- a/app/Models/Logic/IndexLogic.php +++ b/app/Models/Logic/IndexLogic.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Logic; use App\Models\Data\UserData; diff --git a/app/Models/Logic/UserLogic.php b/app/Models/Logic/UserLogic.php index f372c2d9..83ea20a9 100644 --- a/app/Models/Logic/UserLogic.php +++ b/app/Models/Logic/UserLogic.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Models\Logic; use Swoft\Bean\Annotation\Bean; diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php index 57723c20..740df348 100644 --- a/app/Pool/Config/UserPoolConfig.php +++ b/app/Pool/Config/UserPoolConfig.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Pool\Config; use Swoft\Bean\Annotation\Bean; diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index 34e5d259..82e47dfd 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Pool; use Swoft\Bean\Annotation\Inject; diff --git a/app/Process/MyProcess.php b/app/Process/MyProcess.php index 4ac41553..abfd674a 100644 --- a/app/Process/MyProcess.php +++ b/app/Process/MyProcess.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Process; use Swoft\App; diff --git a/app/Services/DemoService.php b/app/Services/DemoService.php index 6cade9dd..8670f67d 100644 --- a/app/Services/DemoService.php +++ b/app/Services/DemoService.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Services; use App\Lib\DemoInterface; diff --git a/app/Services/DemoServiceV2.php b/app/Services/DemoServiceV2.php index adafa101..e5445d10 100644 --- a/app/Services/DemoServiceV2.php +++ b/app/Services/DemoServiceV2.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Services; use App\Lib\DemoInterface; diff --git a/app/Services/MiddlewareService.php b/app/Services/MiddlewareService.php index 1bf6b5d5..2fe429e5 100644 --- a/app/Services/MiddlewareService.php +++ b/app/Services/MiddlewareService.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Services; use App\Lib\MdDemoInterface; diff --git a/app/Swoft.php b/app/Swoft.php index e7cbedc3..161089ee 100644 --- a/app/Swoft.php +++ b/app/Swoft.php @@ -1,9 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. */ + class Swoft extends \Swoft\App { diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index f9ca1554..53e8bcfe 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace App\Tasks; use App\Lib\DemoInterface; diff --git a/bin/bootstrap.php b/bin/bootstrap.php index 5686b502..a49cae19 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + require_once dirname(__DIR__) . '/vendor/autoload.php'; require_once dirname(__DIR__) . '/config/define.php'; diff --git a/config/beans/base.php b/config/beans/base.php index d4110c58..91e0bd60 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'serverDispatcher' => [ 'middlewares' => [ diff --git a/config/beans/console.php b/config/beans/console.php index 05e0b10e..d7acd21c 100644 --- a/config/beans/console.php +++ b/config/beans/console.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ ]; \ No newline at end of file diff --git a/config/beans/log.php b/config/beans/log.php index d871d96f..e0f51fd5 100644 --- a/config/beans/log.php +++ b/config/beans/log.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'noticeHandler' => [ 'class' => \Swoft\Log\FileHandler::class, diff --git a/config/beans/service.php b/config/beans/service.php index 60c0351d..cfbf3dd2 100644 --- a/config/beans/service.php +++ b/config/beans/service.php @@ -1,3 +1,11 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ ]; \ No newline at end of file diff --git a/config/define.php b/config/define.php index f2cc8509..e013c2b8 100644 --- a/config/define.php +++ b/config/define.php @@ -1,6 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + ! defined('DS') && define('DS', DIRECTORY_SEPARATOR); // App name ! defined('APP_NAME') && define('APP_NAME', 'swoft'); diff --git a/config/properties/app.php b/config/properties/app.php index bd48e6bd..eb41508a 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'version' => '1.0', 'autoInitBean' => true, diff --git a/config/properties/breaker.php b/config/properties/breaker.php index 1081a258..b9f1dd2e 100644 --- a/config/properties/breaker.php +++ b/config/properties/breaker.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'user' => [ 'failCount' => 3, diff --git a/config/properties/cache.php b/config/properties/cache.php index 175db1c1..d3a13877 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'redis' => [ 'name' => 'redis', diff --git a/config/properties/db.php b/config/properties/db.php index 2ab37d3f..662b493d 100644 --- a/config/properties/db.php +++ b/config/properties/db.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'master' => [ 'name' => 'master', diff --git a/config/properties/provider.php b/config/properties/provider.php index 87951522..efbf716b 100644 --- a/config/properties/provider.php +++ b/config/properties/provider.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'consul' => [ 'address' => '', diff --git a/config/properties/service.php b/config/properties/service.php index e6d7df42..60b23b57 100644 --- a/config/properties/service.php +++ b/config/properties/service.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'user' => [ 'name' => 'redis', diff --git a/config/server.php b/config/server.php index 9cd4fe82..64af28ec 100644 --- a/config/server.php +++ b/config/server.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'server' => [ 'pfile' => env('PFILE', '/tmp/swoft.pid'), diff --git a/resources/languages/en/default.php b/resources/languages/en/default.php index 1018e758..ac27d49b 100644 --- a/resources/languages/en/default.php +++ b/resources/languages/en/default.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'title' => 'en title' ]; diff --git a/resources/languages/en/msg.php b/resources/languages/en/msg.php index 518324d8..4f09cc56 100644 --- a/resources/languages/en/msg.php +++ b/resources/languages/en/msg.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'body' => 'this is msg [%s] %d', ]; diff --git a/resources/languages/zh/default.php b/resources/languages/zh/default.php index 92a6491d..c0cccbae 100644 --- a/resources/languages/zh/default.php +++ b/resources/languages/zh/default.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'title' => '中文标题' ]; diff --git a/resources/languages/zh/msg.php b/resources/languages/zh/msg.php index a9e822aa..9a3d7bff 100644 --- a/resources/languages/zh/msg.php +++ b/resources/languages/zh/msg.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + return [ 'body' => '这是一条消息 [%s] %d', ]; diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php index a25a1437..670fdc60 100644 --- a/test/Cases/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; use PHPUnit\Framework\TestCase; diff --git a/test/Cases/DemoControllerTest.php b/test/Cases/DemoControllerTest.php index bfe1aee6..c51b6c8e 100644 --- a/test/Cases/DemoControllerTest.php +++ b/test/Cases/DemoControllerTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; diff --git a/test/Cases/IndexControllerTest.php b/test/Cases/IndexControllerTest.php index 5cc5af38..03ad290f 100644 --- a/test/Cases/IndexControllerTest.php +++ b/test/Cases/IndexControllerTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; use Swoft\Http\Message\Testing\Web\Response; diff --git a/test/Cases/MiddlewareTest.php b/test/Cases/MiddlewareTest.php index 6df9dc2a..118a8e1d 100644 --- a/test/Cases/MiddlewareTest.php +++ b/test/Cases/MiddlewareTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; /** diff --git a/test/Cases/RedisControllerTest.php b/test/Cases/RedisControllerTest.php index 27f17759..919bfe88 100644 --- a/test/Cases/RedisControllerTest.php +++ b/test/Cases/RedisControllerTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; use Swoft\Redis\Redis; diff --git a/test/Cases/RestTest.php b/test/Cases/RestTest.php index dfc524ec..0766d48d 100644 --- a/test/Cases/RestTest.php +++ b/test/Cases/RestTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; /** diff --git a/test/Cases/RouteTest.php b/test/Cases/RouteTest.php index 5f2a6d92..4f91a27a 100644 --- a/test/Cases/RouteTest.php +++ b/test/Cases/RouteTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; /** diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php index 37261e17..09246207 100644 --- a/test/Cases/ValidatorControllerTest.php +++ b/test/Cases/ValidatorControllerTest.php @@ -1,5 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Swoft\Test\Cases; /** diff --git a/test/bootstrap.php b/test/bootstrap.php index 74bd6200..62da917e 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,4 +1,12 @@ + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + require_once dirname(__DIR__) . '/vendor/autoload.php'; require_once dirname(__DIR__) . '/config/define.php'; From 5b1e356469408ea2400d670ea4c1920ced2a0fc3 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 12 Mar 2018 17:12:17 +0800 Subject: [PATCH 170/643] Update rules of .php_cs --- .php_cs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.php_cs b/.php_cs index bb32d9d4..9ff88fcc 100644 --- a/.php_cs +++ b/.php_cs @@ -2,23 +2,33 @@ $header = <<<'EOF' This file is part of Swoft. -(c) Swoft -For the full copyright and license information, please view the LICENSE -file that was distributed with this source code. + +@link https://swoft.org +@document https://doc.swoft.org +@contact group@swoft.org +@license https://github.com/swoft-cloud/swoft/blob/master/LICENSE EOF; return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setRules([ - 'header_comment' => ['header' => $header], - 'array_syntax' => ['syntax' => 'short'], - + 'header_comment' => [ + 'commentType' => 'PHPDoc', + 'header' => $header, + 'separate' => 'none' + ], + 'array_syntax' => [ + 'syntax' => 'short' + ], + 'single_quote' => true, ]) - ->setFinder( PhpCsFixer\Finder::create() - ->exclude('vendor') + ->exclude('public') + ->exclude('resources') + ->exclude('config') ->exclude('runtime') + ->exclude('vendor') ->in(__DIR__) ) ->setUsingCache(false); From 3074dfad113989c8b02d5b325d01ca945242472e Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 12 Mar 2018 17:12:25 +0800 Subject: [PATCH 171/643] php-cs-fixer fix --- app/Boot/MyProcess.php | 11 ++++++----- app/Breaker/UserBreaker.php | 11 ++++++----- app/Commands/TestCommand.php | 11 ++++++----- app/Controllers/Admin/DemoController.php | 11 ++++++----- app/Controllers/Api/RestController.php | 11 ++++++----- app/Controllers/DemoController.php | 11 ++++++----- app/Controllers/ExceptionController.php | 19 ++++++++++--------- app/Controllers/HttpClientController.php | 11 ++++++----- app/Controllers/IndexController.php | 13 +++++++------ app/Controllers/MiddlewareController.php | 11 ++++++----- app/Controllers/OrmController.php | 13 +++++++------ app/Controllers/Psr7Controller.php | 11 ++++++----- app/Controllers/RedisController.php | 17 +++++++++-------- app/Controllers/RestController.php | 13 +++++++------ app/Controllers/RouteController.php | 11 ++++++----- app/Controllers/RpcController.php | 11 ++++++----- app/Controllers/SessionController.php | 11 ++++++----- app/Controllers/TaskController.php | 11 ++++++----- app/Controllers/ValidatorController.php | 11 ++++++----- app/Exception/SwoftExceptionHandler.php | 15 ++++++++------- app/Fallback/DemoServiceFallback.php | 13 +++++++------ app/Lib/DemoInterface.php | 13 +++++++------ app/Lib/MdDemoInterface.php | 11 ++++++----- app/Listener/TaskFinish.php | 13 +++++++------ app/Middlewares/ActionTestMiddleware.php | 11 ++++++----- app/Middlewares/ControllerSubMiddleware.php | 11 ++++++----- app/Middlewares/ControllerTestMiddleware.php | 11 ++++++----- app/Middlewares/GroupTestMiddleware.php | 11 ++++++----- app/Middlewares/ServiceMiddleware.php | 11 ++++++----- app/Middlewares/ServiceSubMiddleware.php | 11 ++++++----- app/Middlewares/SubMiddleware.php | 11 ++++++----- app/Middlewares/SubMiddlewares.php | 11 ++++++----- app/Models/Dao/UserDao.php | 11 ++++++----- app/Models/Dao/UserExtDao.php | 11 ++++++----- app/Models/Data/UserData.php | 11 ++++++----- app/Models/Data/UserExtData.php | 11 ++++++----- app/Models/Entity/Count.php | 11 ++++++----- app/Models/Entity/User.php | 13 +++++++------ app/Models/Logic/IndexLogic.php | 11 ++++++----- app/Models/Logic/UserLogic.php | 11 ++++++----- app/Pool/Config/UserPoolConfig.php | 11 ++++++----- app/Pool/UserServicePool.php | 11 ++++++----- app/Process/MyProcess.php | 11 ++++++----- app/Services/DemoService.php | 13 +++++++------ app/Services/DemoServiceV2.php | 13 +++++++------ app/Services/MiddlewareService.php | 11 ++++++----- app/Swoft.php | 11 ++++++----- app/Tasks/SyncTask.php | 19 ++++++++++--------- bin/bootstrap.php | 11 ++++++----- test/Cases/AbstractTestCase.php | 11 ++++++----- test/Cases/DemoControllerTest.php | 11 ++++++----- test/Cases/IndexControllerTest.php | 11 ++++++----- test/Cases/MiddlewareTest.php | 11 ++++++----- test/Cases/RedisControllerTest.php | 11 ++++++----- test/Cases/RestTest.php | 11 ++++++----- test/Cases/RouteTest.php | 11 ++++++----- test/Cases/ValidatorControllerTest.php | 11 ++++++----- test/bootstrap.php | 11 ++++++----- 58 files changed, 370 insertions(+), 312 deletions(-) diff --git a/app/Boot/MyProcess.php b/app/Boot/MyProcess.php index 7391565f..d87fba7d 100644 --- a/app/Boot/MyProcess.php +++ b/app/Boot/MyProcess.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Boot; diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php index 9ff2114e..0c254dac 100644 --- a/app/Breaker/UserBreaker.php +++ b/app/Breaker/UserBreaker.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Breaker; diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php index 9b39dc24..732b373e 100644 --- a/app/Commands/TestCommand.php +++ b/app/Commands/TestCommand.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Commands; diff --git a/app/Controllers/Admin/DemoController.php b/app/Controllers/Admin/DemoController.php index 6680d243..f46c8d09 100644 --- a/app/Controllers/Admin/DemoController.php +++ b/app/Controllers/Admin/DemoController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers\Admin; diff --git a/app/Controllers/Api/RestController.php b/app/Controllers/Api/RestController.php index 5ae8bac0..34cdbc85 100644 --- a/app/Controllers/Api/RestController.php +++ b/app/Controllers/Api/RestController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers\Api; diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index 7af84059..fb4f86bf 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php index 3cb05d00..c4de3dba 100644 --- a/app/Controllers/ExceptionController.php +++ b/app/Controllers/ExceptionController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; @@ -26,7 +27,7 @@ class ExceptionController */ public function exceptioin() { - throw new \Exception("this is exception"); + throw new \Exception('this is exception'); } /** @@ -35,7 +36,7 @@ public function exceptioin() */ public function runtimeException() { - throw new RuntimeException("my exception"); + throw new RuntimeException('my exception'); } /** @@ -44,7 +45,7 @@ public function runtimeException() */ public function defaultException() { - throw new ValidatorException("validator exception! "); + throw new ValidatorException('validator exception! '); } /** @@ -53,6 +54,6 @@ public function defaultException() */ public function viewException() { - throw new BadMethodCallException("view exception! "); + throw new BadMethodCallException('view exception! '); } } \ No newline at end of file diff --git a/app/Controllers/HttpClientController.php b/app/Controllers/HttpClientController.php index 07c3df16..111425de 100644 --- a/app/Controllers/HttpClientController.php +++ b/app/Controllers/HttpClientController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 880b3683..9a94d1c3 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; @@ -195,7 +196,7 @@ public function raw() */ public function exception() { - throw new BadRequestException("bad request exception"); + throw new BadRequestException('bad request exception'); } /** diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php index 64f09f69..3be7f67e 100644 --- a/app/Controllers/MiddlewareController.php +++ b/app/Controllers/MiddlewareController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 80d005a0..cbc28208 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; @@ -57,7 +58,7 @@ public function arSave() } public function test(){ - $sql = "select * from user"; + $sql = 'select * from user'; $em = EntityManager::create(); $result = $em->createQuery($sql)->execute()->getResult(); $em->close(); diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index 48fa082d..aba85a4a 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index dd4119d9..44e7d54e 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; @@ -38,9 +39,9 @@ public function testCache() $result = $this->cache->set('name', 'swoft'); $name = $this->cache->get('name'); - $this->redis->incr("count"); + $this->redis->incr('count'); - $this->redis->incrBy("count2", 2); + $this->redis->incrBy('count2', 2); return [$result, $name, $this->redis->get('count'), $this->redis->get('count2'), '3']; } @@ -111,7 +112,7 @@ public function deleteMultiple() public function has() { - $result = $this->cache->set("name666", 'swoft666'); + $result = $this->cache->set('name666', 'swoft666'); $ret = $this->cache->has('name666'); return [$result, $ret]; diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php index e039165b..4b65e1a6 100644 --- a/app/Controllers/RestController.php +++ b/app/Controllers/RestController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; @@ -48,7 +49,7 @@ public function create(Request $request) $name = $request->input('name'); $bodyParams = $request->getBodyParams(); - $bodyParams = empty($bodyParams) ? ["create", $name] : $bodyParams; + $bodyParams = empty($bodyParams) ? ['create', $name] : $bodyParams; return $bodyParams; } diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 4b47edaf..8321518c 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index 19ce5dcc..f98a391c 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/SessionController.php b/app/Controllers/SessionController.php index e3c8613f..15d5b1f0 100644 --- a/app/Controllers/SessionController.php +++ b/app/Controllers/SessionController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php index 2c27eab8..0fc61779 100644 --- a/app/Controllers/TaskController.php +++ b/app/Controllers/TaskController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Controllers/ValidatorController.php b/app/Controllers/ValidatorController.php index a9dc29ae..124ac93c 100644 --- a/app/Controllers/ValidatorController.php +++ b/app/Controllers/ValidatorController.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Controllers; diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index 3869fdfc..7e9e583d 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Exception; @@ -81,7 +82,7 @@ public function handlerValidatorException(Response $response, \Throwable $throwa { $exception = $throwable->getMessage(); - return $response->json(["message" => $exception]); + return $response->json(['message' => $exception]); } /** @@ -96,7 +97,7 @@ public function handlerBadRequestException(Response $response, \Throwable $throw { $exception = $throwable->getMessage(); - return $response->json(["message" => $exception]); + return $response->json(['message' => $exception]); } /** diff --git a/app/Fallback/DemoServiceFallback.php b/app/Fallback/DemoServiceFallback.php index fa9db9fe..33cdae6f 100644 --- a/app/Fallback/DemoServiceFallback.php +++ b/app/Fallback/DemoServiceFallback.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Fallback; @@ -33,7 +34,7 @@ public function getUser(string $id) return ['fallback', 'getUser', func_get_args()]; } - public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc") + public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = 'desc') { return ['fallback', 'getUserByCond', func_get_args()]; } diff --git a/app/Lib/DemoInterface.php b/app/Lib/DemoInterface.php index 53fc3139..98ef8ff1 100644 --- a/app/Lib/DemoInterface.php +++ b/app/Lib/DemoInterface.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Lib; @@ -42,5 +43,5 @@ public function getUsers(array $ids); */ public function getUser(string $id); - public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc"); + public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = 'desc'); } \ No newline at end of file diff --git a/app/Lib/MdDemoInterface.php b/app/Lib/MdDemoInterface.php index 81288c55..1a2c2664 100644 --- a/app/Lib/MdDemoInterface.php +++ b/app/Lib/MdDemoInterface.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Lib; diff --git a/app/Listener/TaskFinish.php b/app/Listener/TaskFinish.php index 8f71d9c1..f71e2229 100644 --- a/app/Listener/TaskFinish.php +++ b/app/Listener/TaskFinish.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Listener; @@ -26,6 +27,6 @@ class TaskFinish implements EventHandlerInterface */ public function handle(EventInterface $event) { - var_dump("task finish! ", $event->getParams()); + var_dump('task finish! ', $event->getParams()); } } \ No newline at end of file diff --git a/app/Middlewares/ActionTestMiddleware.php b/app/Middlewares/ActionTestMiddleware.php index 780753d4..5abb74a6 100644 --- a/app/Middlewares/ActionTestMiddleware.php +++ b/app/Middlewares/ActionTestMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/ControllerSubMiddleware.php b/app/Middlewares/ControllerSubMiddleware.php index 382ea7b8..4c10c72e 100644 --- a/app/Middlewares/ControllerSubMiddleware.php +++ b/app/Middlewares/ControllerSubMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/ControllerTestMiddleware.php b/app/Middlewares/ControllerTestMiddleware.php index 10debae1..ee6d2c82 100644 --- a/app/Middlewares/ControllerTestMiddleware.php +++ b/app/Middlewares/ControllerTestMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/GroupTestMiddleware.php b/app/Middlewares/GroupTestMiddleware.php index 2a080897..83458e92 100644 --- a/app/Middlewares/GroupTestMiddleware.php +++ b/app/Middlewares/GroupTestMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/ServiceMiddleware.php b/app/Middlewares/ServiceMiddleware.php index 0ede6794..bbc7f5c0 100644 --- a/app/Middlewares/ServiceMiddleware.php +++ b/app/Middlewares/ServiceMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/ServiceSubMiddleware.php b/app/Middlewares/ServiceSubMiddleware.php index 8b4c199b..32b42439 100644 --- a/app/Middlewares/ServiceSubMiddleware.php +++ b/app/Middlewares/ServiceSubMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/SubMiddleware.php b/app/Middlewares/SubMiddleware.php index a4259bb5..5b830a93 100644 --- a/app/Middlewares/SubMiddleware.php +++ b/app/Middlewares/SubMiddleware.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Middlewares/SubMiddlewares.php b/app/Middlewares/SubMiddlewares.php index 99495b82..159340c5 100644 --- a/app/Middlewares/SubMiddlewares.php +++ b/app/Middlewares/SubMiddlewares.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Middlewares; diff --git a/app/Models/Dao/UserDao.php b/app/Models/Dao/UserDao.php index 950d227c..e788180d 100644 --- a/app/Models/Dao/UserDao.php +++ b/app/Models/Dao/UserDao.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Dao; diff --git a/app/Models/Dao/UserExtDao.php b/app/Models/Dao/UserExtDao.php index e79027b0..89de9aa4 100644 --- a/app/Models/Dao/UserExtDao.php +++ b/app/Models/Dao/UserExtDao.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Dao; diff --git a/app/Models/Data/UserData.php b/app/Models/Data/UserData.php index 55c854f3..89199717 100644 --- a/app/Models/Data/UserData.php +++ b/app/Models/Data/UserData.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Data; diff --git a/app/Models/Data/UserExtData.php b/app/Models/Data/UserExtData.php index fb0e5e0b..d7100060 100644 --- a/app/Models/Data/UserExtData.php +++ b/app/Models/Data/UserExtData.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Data; diff --git a/app/Models/Entity/Count.php b/app/Models/Entity/Count.php index 5ab7066f..b7785622 100644 --- a/app/Models/Entity/Count.php +++ b/app/Models/Entity/Count.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Entity; diff --git a/app/Models/Entity/User.php b/app/Models/Entity/User.php index 5bb238d7..3ecf8d18 100644 --- a/app/Models/Entity/User.php +++ b/app/Models/Entity/User.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Entity; @@ -70,7 +71,7 @@ class User extends Model * @Column(name="description", type="string") * @var string */ - private $desc = ""; + private $desc = ''; /** * 非数据库字段,未定义映射关系 diff --git a/app/Models/Logic/IndexLogic.php b/app/Models/Logic/IndexLogic.php index 45d2a482..9cd849e9 100644 --- a/app/Models/Logic/IndexLogic.php +++ b/app/Models/Logic/IndexLogic.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Logic; diff --git a/app/Models/Logic/UserLogic.php b/app/Models/Logic/UserLogic.php index 83ea20a9..b1783302 100644 --- a/app/Models/Logic/UserLogic.php +++ b/app/Models/Logic/UserLogic.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Models\Logic; diff --git a/app/Pool/Config/UserPoolConfig.php b/app/Pool/Config/UserPoolConfig.php index 740df348..8e641581 100644 --- a/app/Pool/Config/UserPoolConfig.php +++ b/app/Pool/Config/UserPoolConfig.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Pool\Config; diff --git a/app/Pool/UserServicePool.php b/app/Pool/UserServicePool.php index 82e47dfd..dfdcf1d1 100644 --- a/app/Pool/UserServicePool.php +++ b/app/Pool/UserServicePool.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Pool; diff --git a/app/Process/MyProcess.php b/app/Process/MyProcess.php index abfd674a..2b5ad939 100644 --- a/app/Process/MyProcess.php +++ b/app/Process/MyProcess.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Process; diff --git a/app/Services/DemoService.php b/app/Services/DemoService.php index 8670f67d..3bde2d77 100644 --- a/app/Services/DemoService.php +++ b/app/Services/DemoService.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Services; @@ -51,7 +52,7 @@ public function getUser(string $id) * @param string $desc default value * @return array */ - public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc") + public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = 'desc') { return [$type, $uid, $name, $price, $desc]; } diff --git a/app/Services/DemoServiceV2.php b/app/Services/DemoServiceV2.php index e5445d10..eda1de26 100644 --- a/app/Services/DemoServiceV2.php +++ b/app/Services/DemoServiceV2.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Services; @@ -50,7 +51,7 @@ public function getUser(string $id) * @param string $desc default value * @return array */ - public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = "desc") + public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = 'desc') { return [$type, $uid, $name, $price, $desc]; } diff --git a/app/Services/MiddlewareService.php b/app/Services/MiddlewareService.php index 2fe429e5..42d12833 100644 --- a/app/Services/MiddlewareService.php +++ b/app/Services/MiddlewareService.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Services; diff --git a/app/Swoft.php b/app/Swoft.php index 161089ee..944bfd14 100644 --- a/app/Swoft.php +++ b/app/Swoft.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ class Swoft extends \Swoft\App diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index 55050204..440a6035 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace App\Tasks; @@ -48,11 +49,11 @@ class SyncTask */ public function deliverCo(string $p1, string $p2) { - App::profileStart("co"); + App::profileStart('co'); App::trace('trace'); App::info('info'); App::pushlog('key', 'stelin'); - App::profileEnd("co"); + App::profileEnd('co'); return sprintf('deliverCo-%s-%s', $p1, $p2); } @@ -67,11 +68,11 @@ public function deliverCo(string $p1, string $p2) */ public function deliverAsync(string $p1, string $p2) { - App::profileStart("co"); + App::profileStart('co'); App::trace('trace'); App::info('info'); App::pushlog('key', 'stelin'); - App::profileEnd("co"); + App::profileEnd('co'); return sprintf('deliverCo-%s-%s', $p1, $p2); } diff --git a/bin/bootstrap.php b/bin/bootstrap.php index a49cae19..9b1a4183 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ require_once dirname(__DIR__) . '/vendor/autoload.php'; diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php index 670fdc60..f29c922d 100644 --- a/test/Cases/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/DemoControllerTest.php b/test/Cases/DemoControllerTest.php index c51b6c8e..b5b97772 100644 --- a/test/Cases/DemoControllerTest.php +++ b/test/Cases/DemoControllerTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/IndexControllerTest.php b/test/Cases/IndexControllerTest.php index 03ad290f..0fcdcaba 100644 --- a/test/Cases/IndexControllerTest.php +++ b/test/Cases/IndexControllerTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/MiddlewareTest.php b/test/Cases/MiddlewareTest.php index 118a8e1d..7f58d35b 100644 --- a/test/Cases/MiddlewareTest.php +++ b/test/Cases/MiddlewareTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/RedisControllerTest.php b/test/Cases/RedisControllerTest.php index 919bfe88..3a70d92c 100644 --- a/test/Cases/RedisControllerTest.php +++ b/test/Cases/RedisControllerTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/RestTest.php b/test/Cases/RestTest.php index 0766d48d..cefdbbe7 100644 --- a/test/Cases/RestTest.php +++ b/test/Cases/RestTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/RouteTest.php b/test/Cases/RouteTest.php index 4f91a27a..d42ac088 100644 --- a/test/Cases/RouteTest.php +++ b/test/Cases/RouteTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php index 09246207..e856fbd9 100644 --- a/test/Cases/ValidatorControllerTest.php +++ b/test/Cases/ValidatorControllerTest.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ namespace Swoft\Test\Cases; diff --git a/test/bootstrap.php b/test/bootstrap.php index 62da917e..1d450773 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,10 +1,11 @@ - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. + * + * @link https://swoft.org + * @document https://doc.swoft.org + * @contact group@swoft.org + * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ require_once dirname(__DIR__) . '/vendor/autoload.php'; From 9f3063bfc966ea3b71e3823452a3f5405fbfda7c Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Mon, 12 Mar 2018 18:59:50 +0800 Subject: [PATCH 172/643] swoole enable http2 --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b67ba368..472c07e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN apt-get update \ zip \ libz-dev \ libssl-dev \ + libnghttp2-dev \ && apt-get clean \ && apt-get autoremove @@ -41,7 +42,7 @@ RUN wget https://github.com/swoole/swoole-src/archive/v2.1.1.tar.gz -O swoole.ta && ( \ cd swoole \ && phpize \ - && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine --enable-openssl \ + && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine --enable-openssl --enable-http2 \ && make -j$(nproc) \ && make install \ ) \ From f7737b83fe338c07bd60f9925d5247f126f112b9 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 14 Mar 2018 01:58:26 +0800 Subject: [PATCH 173/643] add demo --- app/Controllers/HttpClientController.php | 4 +- app/Controllers/OrmController.php | 120 ++++++++++++++++++++++- app/Controllers/RedisController.php | 18 ++++ 3 files changed, 135 insertions(+), 7 deletions(-) diff --git a/app/Controllers/HttpClientController.php b/app/Controllers/HttpClientController.php index 0c7fcc17..f4e777ca 100644 --- a/app/Controllers/HttpClientController.php +++ b/app/Controllers/HttpClientController.php @@ -19,8 +19,8 @@ class HttpClientController public function request(): array { $client = new Client(); - $result = $client->get('https://www.swoft.org/')->getResult(); - $result2 = $client->get('https://www.swoft.org/')->getResponse()->getBody()->getContents(); + $result = $client->get('http://www.swoft.org/')->getResult(); + $result2 = $client->get('http://www.swoft.org/')->getResponse()->getBody()->getContents(); return compact('result', 'result2'); } } \ No newline at end of file diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 2019d2e6..8cafc587 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -49,15 +49,126 @@ public function arSave() return [$userResult, $countResult, $directUser, $directCount]; } - public function test(){ - $sql = "select * from user"; + public function ntotClose(){ + $sql = "select * from user limit 2"; $em = EntityManager::create(); $result = $em->createQuery($sql)->execute()->getResult(); - $em->close(); +// $em->close(); return [$result]; } + public function notGetResult(){ + + $user2 = User::findById(4212)->getResult(); + $user = User::findById(4212); + return [$user2]; + } + + /** + * @return array + */ + public function notCommitAndClose() + { + $user2 = User::findById(4212)->getResult(); + + $user = new User(); + $user->setName('stelin'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $em = EntityManager::create(); + $em->beginTransaction(); + $result = $em->save($user)->getResult(); + + return [$user2, $result]; + } + + /** + * @return array + */ + public function notCommitAndCloseAndGetResult() + { + $user2 = User::findById(4212)->getResult(); + + $user = new User(); + $user->setName('stelin'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $em = EntityManager::create(); + $em->beginTransaction(); + $result = $em->save($user); + + return [$user2, $result]; + } + + /** + * @return array + */ + public function notCommit() + { + $user2 = User::findById(4212)->getResult(); + + $user = new User(); + $user->setName('stelin'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $em = EntityManager::create(); + $em->beginTransaction(); + $result = $em->save($user)->getResult(); + $em->close(); + + return [$user2, $result]; + } + + /** + * @return array + */ + public function notCloseButCommit() + { + $user2 = User::findById(4212)->getResult(); + + $user = new User(); + $user->setName('stelin'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $em = EntityManager::create(); + $em->beginTransaction(); + $result = $em->save($user)->getResult(); + $em->commit(); + + return [$user2, $result]; + } + + /** + * @return array + */ + public function closeAndNotGetResult() + { + $user = new User(); + $user->setName('stelin'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $em = EntityManager::create(); + $user2 = User::findById(4212)->getResult(); + $em->beginTransaction(); + $result = $em->save($user)->getResult(); + $result = $em->save($user); + $em->commit(); + $em->close(); + + return [$user2, $result]; + } + /** * EM查找 */ @@ -324,10 +435,9 @@ public function query() ->orderBy('u.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); // $result = $query->getResult(); $result = $result->getResult(); - $sql = $query->getSql(); $em->close(); - return [$result, $sql]; + return [$result]; } /** diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 8dbca636..9ad0ad00 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -46,6 +46,14 @@ public function testRedis() return [$result, $name]; } + public function error() + { + $result = $this->redis->set('nameRedis', 'swoft2'); + $name = $this->redis->get('nameRedis'); + $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); + return [$name]; + } + public function ab() { $result1 = User::query()->select('*')->where('id', '720')->limit(1)->execute()->getResult(); @@ -54,6 +62,16 @@ public function ab() return [$result1, $result2]; } + public function ab2() + { + var_dump($this->redis->incr("count")); + var_dump($this->redis->incr("count")); + var_dump($this->redis->incr("count")); + var_dump($this->redis->incr("count")); + $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); + return ['ab']; + } + public function testFunc() { $result = cache()->set('nameFunc', 'swoft3'); From 9c52ee86d36f2379d52e5dce568707b1672b5f7c Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 14 Mar 2018 20:00:18 +0800 Subject: [PATCH 174/643] update: update composer.json, fix packagist config --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index 1a39e870..edb76e1c 100644 --- a/composer.json +++ b/composer.json @@ -53,10 +53,10 @@ ], "test": "./vendor/bin/phpunit -c phpunit.xml" }, - "repositories": [ - { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" - } - ] + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" + } + } } From 3c2fe900583b9f20a2052ff8e1125807cc2330b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=88=90=E9=83=BD?= Date: Thu, 15 Mar 2018 21:26:28 +0800 Subject: [PATCH 175/643] =?UTF-8?q?=E5=A2=9E=E5=8A=A0env=E5=88=AB=E5=90=8D?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=94=AF=E6=8C=81=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89env=E6=96=87=E4=BB=B6=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/define.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/define.php b/config/define.php index e013c2b8..2506af09 100644 --- a/config/define.php +++ b/config/define.php @@ -16,6 +16,7 @@ // Register alias $aliases = [ '@root' => BASE_PATH, + '@env' => '@root', '@app' => '@root/app', '@res' => '@root/resources', '@runtime' => '@root/runtime', From a8c6f08897b79c874f04e553036a20d0ee67b8a9 Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 16 Mar 2018 17:08:51 +0800 Subject: [PATCH 176/643] fix: error option name for http router --- config/beans/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/beans/base.php b/config/beans/base.php index 9827b9bf..ed4e7700 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -15,7 +15,7 @@ ] ], 'httpRouter' => [ - 'ignoreLastSep' => false, + 'ignoreLastSlash' => false, 'tmpCacheNumber' => 1000, 'matchAll' => '', ], From 7b9a5c7299d35f9f643f4a3dcb177fce3234294d Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Sat, 17 Mar 2018 04:40:24 +0800 Subject: [PATCH 177/643] Update Dockerfile Added PDO Mysql extension --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 472c07e4..c692d568 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,8 @@ RUN curl -sS https://getcomposer.org/installer | php \ RUN pecl install redis && docker-php-ext-enable redis && pecl clear-cache +RUN docker-php-ext-install pdo_mysql + RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz \ && mkdir -p hiredis \ && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \ From e44c32419209bad5c786eb024f430008bc331f44 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Sun, 18 Mar 2018 22:53:35 +0800 Subject: [PATCH 178/643] Default closing log --- .env.example | 4 +++ app/Controllers/IndexController.php | 11 ++++++++ app/Controllers/OrmController.php | 6 +++++ app/Controllers/RedisController.php | 17 ++++++++++++ app/Pool/Config/DemoRedisPoolConfig.php | 35 +++++++++++++++++++++++++ app/Pool/DemoRedisPool.php | 30 +++++++++++++++++++++ config/beans/base.php | 4 +++ config/beans/log.php | 7 ++--- config/properties/cache.php | 7 ++++- 9 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 app/Pool/Config/DemoRedisPoolConfig.php create mode 100644 app/Pool/DemoRedisPool.php diff --git a/.env.example b/.env.example index 689e2f1f..fff4b45b 100644 --- a/.env.example +++ b/.env.example @@ -68,6 +68,10 @@ REDIS_MAX_IDLE_TIME=60 REDIS_TIMEOUT=3 REDIS_SERIALIZE=1 +# other redis node +REDIS_DEMO_REDIS_DB=6 +REDIS_DEMO_REDIS_PREFIX=demo_redis_ + # User service (demo service) USER_POOL_NAME=user USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099 diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 9a94d1c3..4a817832 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -10,8 +10,10 @@ namespace App\Controllers; +use Swoft\App; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; +use Swoft\Log\Log; use Swoft\View\Bean\Annotation\View; use Swoft\Contract\Arrayable; use Swoft\Http\Server\Exception\BadRequestException; @@ -190,6 +192,15 @@ public function raw() return $name; } + public function testLog() + { + App::trace('this is app trace'); + Log::trace('this is log trace'); + App::error('this is log error'); + Log::trace('this is log error'); + return ['log']; + } + /** * @RequestMapping() * @throws \Swoft\Http\Server\Exception\BadRequestException diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 727af3ce..0fd52216 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -196,6 +196,12 @@ public function save() return [$result]; } + public function forceMater() + { + $user = User::findById(4212, 'default.master')->getResult(); + return [$user]; + } + /** * 实体内容删除 */ diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index f7b15529..293c1e10 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -34,6 +34,23 @@ class RedisController */ private $redis; + /** + * @Inject("demoRedis") + * @var \Swoft\Redis\Redis + */ + private $demoRedis; + + public function testDemoRedis() + { + $result = $this->demoRedis->set('name', 'swoft'); + $name = $this->demoRedis->get('name'); + + $this->demoRedis->incr('count'); + $this->demoRedis->incrBy('count2', 2); + + return [$result, $name, $this->demoRedis->get('count'), $this->demoRedis->get('count2'), '3']; + } + public function testCache() { $result = $this->cache->set('name', 'swoft'); diff --git a/app/Pool/Config/DemoRedisPoolConfig.php b/app/Pool/Config/DemoRedisPoolConfig.php new file mode 100644 index 00000000..087b427c --- /dev/null +++ b/app/Pool/Config/DemoRedisPoolConfig.php @@ -0,0 +1,35 @@ + [ 'driver' => 'redis', ], + 'demoRedis' => [ + 'class' => \Swoft\Redis\Redis::class, + 'poolName' => 'demoRedis' + ] ]; diff --git a/config/beans/log.php b/config/beans/log.php index 7e7e18ba..732e5e06 100644 --- a/config/beans/log.php +++ b/config/beans/log.php @@ -28,10 +28,11 @@ \Swoft\Log\Logger::WARNING, ], ], - 'logger' => [ + 'logger' => [ 'name' => APP_NAME, - 'flushInterval' => 100000, - 'flushRequest' => false, + 'enable' => false, + 'flushInterval' => 100, + 'flushRequest' => true, 'handlers' => [ '${noticeHandler}', '${applicationHandler}', diff --git a/config/properties/cache.php b/config/properties/cache.php index d3a13877..28328aff 100644 --- a/config/properties/cache.php +++ b/config/properties/cache.php @@ -8,7 +8,7 @@ */ return [ - 'redis' => [ + 'redis' => [ 'name' => 'redis', 'uri' => [ '127.0.0.1:6379', @@ -21,6 +21,11 @@ 'maxIdleTime' => 60, 'timeout' => 8, 'db' => 1, + 'prefix' => 'redis_', 'serialize' => 0, ], + 'demoRedis' => [ + 'db' => 2, + 'prefix' => 'demo_redis_', + ], ]; \ No newline at end of file From 8586e0dfe23929ccd685a9e6da454538536ab4bb Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 21 Mar 2018 10:41:34 +0800 Subject: [PATCH 179/643] up: add example route /routes access /routes you can see all registered routes. --- app/Controllers/RouteController.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php index 8321518c..6e9a3d05 100644 --- a/app/Controllers/RouteController.php +++ b/app/Controllers/RouteController.php @@ -30,6 +30,23 @@ public function index() return 'index'; } + /** + * access /routes you can see all registered routes. + * @RequestMapping("/routes") + */ + public function routes(): array + { + /** @var \Swoft\Http\Server\Router\HandlerMapping $router */ + $router = \bean('httpRouter'); + + return [ + 'static' => $router->getStaticRoutes(), + 'regular' => $router->getRegularRoutes(), + 'vague' => $router->getVagueRoutes(), + 'cached' => $router->getCacheRoutes(), + ]; + } + /** * @RequestMapping(route="user/{uid}/book/{bid}/{bool}/{name}") * @@ -137,4 +154,4 @@ public function behind(Request $request) { return [get_class($request)]; } -} \ No newline at end of file +} From 2ee51589127f81d4d3ec8d240de24d90c09d9fc1 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 21 Mar 2018 19:31:11 +0800 Subject: [PATCH 180/643] add path alias for vendor dir --- config/define.php | 1 + 1 file changed, 1 insertion(+) diff --git a/config/define.php b/config/define.php index 2506af09..666495b4 100644 --- a/config/define.php +++ b/config/define.php @@ -26,6 +26,7 @@ '@properties' => '@configs/properties', '@console' => '@beans/console.php', '@commands' => '@app/command', + '@vendor' => '@root/vendor', ]; \Swoft\App::setAliases($aliases); From 0666326f035630739a9f7970a80342aff4f35c72 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 21 Mar 2018 19:32:34 +0800 Subject: [PATCH 181/643] up: add ws settings --- config/server.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config/server.php b/config/server.php index 35085e57..714d03db 100644 --- a/config/server.php +++ b/config/server.php @@ -29,6 +29,12 @@ 'mode' => env('HTTP_MODE', SWOOLE_PROCESS), 'type' => env('HTTP_TYPE', SWOOLE_SOCK_TCP), ], + 'ws' => [ + // enable handler http request ? + 'enable_http' => true, + // other settings will extend the 'http' config + // you can define separately to overwrite existing settings + ], 'crontab' => [ 'task_count' => env('CRONTAB_TASK_COUNT', 1024), 'task_queue' => env('CRONTAB_TASK_QUEUE', 2048), @@ -48,4 +54,4 @@ 'ssl_cert_file' => env('SSL_CERT_FILE', ''), 'ssl_key_file' => env('SSL_KEY_FILE', ''), ], -]; \ No newline at end of file +]; From 38688ca07663002113ac3f600f3bf76ad9b4d10c Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 21 Mar 2018 19:34:02 +0800 Subject: [PATCH 182/643] Update server.php --- config/server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/server.php b/config/server.php index 714d03db..584b0c05 100644 --- a/config/server.php +++ b/config/server.php @@ -30,7 +30,7 @@ 'type' => env('HTTP_TYPE', SWOOLE_SOCK_TCP), ], 'ws' => [ - // enable handler http request ? + // enable handle http request ? 'enable_http' => true, // other settings will extend the 'http' config // you can define separately to overwrite existing settings From 75a8c6d4be61e33b83b307b79099dc055cd9cfec Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Wed, 21 Mar 2018 19:52:05 +0800 Subject: [PATCH 183/643] validator and console task --- .env.example | 3 +++ app/Commands/TestCommand.php | 26 +++++++++++++++++++++++++- app/Controllers/IndexController.php | 2 +- app/Tasks/SyncTask.php | 10 ++++++++-- config/server.php | 9 ++++++--- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index fff4b45b..e42feb1d 100644 --- a/.env.example +++ b/.env.example @@ -29,6 +29,9 @@ WORKER_NUM=1 MAX_REQUEST=100000 DAEMONIZE=0 DISPATCH_MODE=2 +TASK_IPC_MODE=3 +MESSAGE_QUEUE_KEY=1879052289 +TASK_TMPDIR=/tmp/ LOG_FILE=@runtime/logs/swoole.log TASK_WORKER_NUM=1 PACKAGE_MAX_LENGTH=2048 diff --git a/app/Commands/TestCommand.php b/app/Commands/TestCommand.php index 732b373e..3e069905 100644 --- a/app/Commands/TestCommand.php +++ b/app/Commands/TestCommand.php @@ -18,11 +18,12 @@ use Swoft\Console\Output\Output; use Swoft\Core\Coroutine; use Swoft\Log\Log; +use Swoft\Task\Task; /** * Test command * - * @Command(coroutine=true) + * @Command(coroutine=false) */ class TestCommand { @@ -88,4 +89,27 @@ public function demo() $data = $logic->getUserInfo(['uid1']); var_dump($hasOpt, $opt, $name, $data); } + + /** + * this task command + * + * @Usage + * test:{command} [arguments] [options] + * + * @Options + * -o,--o this is command option + * + * @Arguments + * arg this is argument + * + * @Example + * php swoft test:task + * + * @Mapping() + */ + public function task() + { + $result = Task::deliver('sync', 'console', ['console']); + var_dump($result); + } } \ No newline at end of file diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 4a817832..22702891 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -11,6 +11,7 @@ namespace App\Controllers; use Swoft\App; +use Swoft\Core\Coroutine; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Log\Log; @@ -219,5 +220,4 @@ public function redirect(Response $response): Response { return $response->redirect('/'); } - } diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index 440a6035..f93b9d1b 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -95,9 +95,9 @@ public function cache() * @return array */ public function mysql(){ - $result = User::findById(720)->getResult(); + $result = User::findById(4212)->getResult(); - $query = User::findById(720); + $query = User::findById(4212); /* @var User $user */ $user = $query->getResult(User::class); @@ -120,6 +120,12 @@ public function http() return $data; } + public function console(string $data) + { + var_dump('console', $data); + return ['console']; + } + /** * Rpc task * diff --git a/config/server.php b/config/server.php index 35085e57..6de3397a 100644 --- a/config/server.php +++ b/config/server.php @@ -44,8 +44,11 @@ 'upload_tmp_dir' => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'), 'document_root' => env('DOCUMENT_ROOT', BASE_PATH . '/public'), 'enable_static_handler' => env('ENABLE_STATIC_HANDLER', true), - 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), - 'ssl_cert_file' => env('SSL_CERT_FILE', ''), - 'ssl_key_file' => env('SSL_KEY_FILE', ''), + 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), + 'ssl_cert_file' => env('SSL_CERT_FILE', ''), + 'ssl_key_file' => env('SSL_KEY_FILE', ''), + 'task_ipc_mode' => env('TASK_IPC_MODE', 3), + 'message_queue_key' => env('MESSAGE_QUEUE_KEY', 0x70001001), + 'task_tmpdir' => env('TASK_TMPDIR', '/tmp'), ], ]; \ No newline at end of file From b1fdede52bbd47c8cb29f2876f88dc11665b63fb Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Thu, 22 Mar 2018 07:59:10 +0800 Subject: [PATCH 184/643] Update define.php Format --- config/define.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/define.php b/config/define.php index 666495b4..2a9a32d2 100644 --- a/config/define.php +++ b/config/define.php @@ -26,7 +26,7 @@ '@properties' => '@configs/properties', '@console' => '@beans/console.php', '@commands' => '@app/command', - '@vendor' => '@root/vendor', + '@vendor' => '@root/vendor', ]; \Swoft\App::setAliases($aliases); From d20761bc7401d5254dd539296e76bfa16b9d6921 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Thu, 22 Mar 2018 07:59:38 +0800 Subject: [PATCH 185/643] Update server.php --- config/server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/server.php b/config/server.php index 584b0c05..753b3d41 100644 --- a/config/server.php +++ b/config/server.php @@ -31,7 +31,7 @@ ], 'ws' => [ // enable handle http request ? - 'enable_http' => true, + 'enable_http' => env('WS_ENABLE_HTTP', true), // other settings will extend the 'http' config // you can define separately to overwrite existing settings ], From cd281432a7b1fb0e39c19e3f9b3ef1fe8170b8fb Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 23 Mar 2018 17:12:44 +0800 Subject: [PATCH 186/643] task and rpc --- .env.example | 2 +- app/Tasks/SyncTask.php | 8 +++++++- config/server.php | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index e42feb1d..35323b55 100644 --- a/.env.example +++ b/.env.example @@ -29,7 +29,7 @@ WORKER_NUM=1 MAX_REQUEST=100000 DAEMONIZE=0 DISPATCH_MODE=2 -TASK_IPC_MODE=3 +TASK_IPC_MODE=2 MESSAGE_QUEUE_KEY=1879052289 TASK_TMPDIR=/tmp/ LOG_FILE=@runtime/logs/swoole.log diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index f93b9d1b..ec68852e 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -133,7 +133,13 @@ public function console(string $data) */ public function rpc() { - return $this->demoService->getUser('6666'); + $user = $this->demoService->getUser('6666'); + $defer1 = $this->demoService->deferGetUser('666'); + $defer2 = $this->demoService->deferGetUser('888'); + + $result1 = $defer1->getResult(); + $result2 = $defer2->getResult(); + return [$user, $result1, $result2]; } /** diff --git a/config/server.php b/config/server.php index 6de3397a..30288b6c 100644 --- a/config/server.php +++ b/config/server.php @@ -47,7 +47,7 @@ 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), 'ssl_cert_file' => env('SSL_CERT_FILE', ''), 'ssl_key_file' => env('SSL_KEY_FILE', ''), - 'task_ipc_mode' => env('TASK_IPC_MODE', 3), + 'task_ipc_mode' => env('TASK_IPC_MODE', 2), 'message_queue_key' => env('MESSAGE_QUEUE_KEY', 0x70001001), 'task_tmpdir' => env('TASK_TMPDIR', '/tmp'), ], From 7f565d70b3205a421dd250a0dfbc83ee8715b8f3 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 26 Mar 2018 16:20:30 +0800 Subject: [PATCH 187/643] up: update some config for websocket --- .env.example | 5 ++- .gitignore | 3 +- app/WebSocket/EchoController.php | 54 ++++++++++++++++++++++++++++++++ composer.json | 3 +- config/beans/base.php | 3 +- config/properties/app.php | 3 +- phar.build.inc | 41 ++++++++++++++++++++++++ 7 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 app/WebSocket/EchoController.php create mode 100644 phar.build.inc diff --git a/.env.example b/.env.example index 35323b55..3a69ebb9 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,9 @@ HTTP_PORT=80 HTTP_MODE=SWOOLE_PROCESS HTTP_TYPE=SWOOLE_SOCK_TCP +# WebSocket +WS_ENABLE_HTTP=true + # TCP TCP_HOST=0.0.0.0 TCP_PORT=8099 @@ -103,4 +106,4 @@ CONSUL_REGISTER_SERVICE_PORT=8099 CONSUL_REGISTER_CHECK_NAME=user CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099 CONSUL_REGISTER_CHECK_INTERVAL=10 -CONSUL_REGISTER_CHECK_TIMEOUT=1 \ No newline at end of file +CONSUL_REGISTER_CHECK_TIMEOUT=1 diff --git a/.gitignore b/.gitignore index 579da2f0..36b10d75 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ vendor/ temp/ *.lock .phpintel/ -.env \ No newline at end of file +.env +.DS_Store diff --git a/app/WebSocket/EchoController.php b/app/WebSocket/EchoController.php new file mode 100644 index 00000000..93204b1b --- /dev/null +++ b/app/WebSocket/EchoController.php @@ -0,0 +1,54 @@ +push($fd, 'hello, welcome! :)'); + } + + /** + * @param Server $server + * @param Frame $frame + */ + public function onMessage(Server $server, Frame $frame) + { + $server->push($frame->fd, 'hello, I have received your message: ' . $frame->data); + } + + /** + * @param Server $server + * @param int $fd + */ + public function onClose(Server $server, int $fd) + { + $server->push($fd, 'oo, goodbye! :)'); + } +} diff --git a/composer.json b/composer.json index edb76e1c..0781e45d 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,8 @@ }, "autoload": { "psr-4": { - "App\\": "app/" + "App\\": "app/", + "Swoft\\WebSocket\\Server\\": "vendor/swoft/websocket-server/src/" }, "files": [ "app/Swoft.php" diff --git a/config/beans/base.php b/config/beans/base.php index ff2983ac..8711d971 100644 --- a/config/beans/base.php +++ b/config/beans/base.php @@ -11,7 +11,8 @@ 'serverDispatcher' => [ 'middlewares' => [ \Swoft\View\Middleware\ViewMiddleware::class, - //\Swoft\Session\Middleware\SessionMiddleware::class, + // \Swoft\Devtool\Middleware\DevToolMiddleware::class, + // \Swoft\Session\Middleware\SessionMiddleware::class, ] ], 'httpRouter' => [ diff --git a/config/properties/app.php b/config/properties/app.php index eb41508a..6e3cb07a 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -26,6 +26,7 @@ 'App\Listener', 'App\Process', 'App\Fallback', + 'App\WebSocket', ], 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', @@ -36,4 +37,4 @@ 'service' => require __DIR__ . DS . 'service.php', 'breaker' => require __DIR__ . DS . 'breaker.php', 'provider' => require __DIR__ . DS . 'provider.php', -]; \ No newline at end of file +]; diff --git a/phar.build.inc b/phar.build.inc new file mode 100644 index 00000000..31640221 --- /dev/null +++ b/phar.build.inc @@ -0,0 +1,41 @@ +stripComments(true) + ->setShebang(true) + ->addSuffix('.json')// for add composer.json + ->addExclude([ + 'test', + 'tests', + 'runtime', + 'eaglewu', + ]) + ->addFile([ + 'LICENSE', + 'README.md', + ]) + ->setCliIndex('bin/swoft') + // ->setWebIndex('web/index.php') + // ->setVersionFile('config/config.php') +; + +// Command Controller 命令类不去除注释,注释上是命令帮助信息 +$compiler->setStripFilter(function ($file) { + /** @var \SplFileInfo $file */ + $path = $file->getPath(); + + if (strpos($path, 'swoft')) { + return false; + } + + return false === strpos($file->getFilename(), 'Command.php'); +}); From ea14dcd6c63706877bdda8b0797dd81276773f20 Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 26 Mar 2018 23:00:32 +0800 Subject: [PATCH 188/643] Update composer.json --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 0781e45d..47835412 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "swoft/rpc-client": "^1.0", "swoft/http-server": "^1.0", "swoft/http-client": "^1.0", + "swoft/websocket-server": "^1.0", "swoft/task": "^1.0", "swoft/http-message": "^1.0", "swoft/view": "^1.0", From c7f8ace97942e56d1019fb3883d8fdea942af6d5 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 27 Mar 2018 14:59:30 +0800 Subject: [PATCH 189/643] Update composer.json --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 47835412..c73590b8 100644 --- a/composer.json +++ b/composer.json @@ -37,8 +37,7 @@ }, "autoload": { "psr-4": { - "App\\": "app/", - "Swoft\\WebSocket\\Server\\": "vendor/swoft/websocket-server/src/" + "App\\": "app/" }, "files": [ "app/Swoft.php" From 92f46d71c756318f051dc0f7a7b7244f3bd3c02b Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 28 Mar 2018 09:17:23 +0800 Subject: [PATCH 190/643] Update README.md --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4cf3d59b..1549db7d 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,22 @@

-[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) -[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) -[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) -[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) -[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +

+ + Latest Version + + Build Status + + Php Version + + Swoole Version + + Hiredis Version + + Swoft Doc + + Swoft License +

# 简介 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 From b6a3192a7fb2e4e3c4c64e7cb5a194fade4b5ac5 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 28 Mar 2018 09:22:12 +0800 Subject: [PATCH 191/643] Update README.md --- README.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1549db7d..aac7540b 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ - 基于 Swoole 扩展 - 内置协程网络服务器 +- webSocket 服务器 - 强大的 AOP (面向切面编程) - 灵活完善的注解功能 - 全局的依赖注入容器 @@ -58,7 +59,7 @@ QQ 交流群: 548173319 # 环境要求 1. PHP 7.0 + -2. [Swoole 2.0.12](https://github.com/swoole/swoole-src/releases) +, 需开启协程和异步Redis +2. [Swoole 2.1.1](https://github.com/swoole/swoole-src/releases) +, 需开启协程和异步Redis 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) @@ -74,9 +75,11 @@ QQ 交流群: 548173319 * `composer create-project swoft/swoft swoft` ## Docker 安装 + * `docker run -p 80:80 swoft/swoft` ## Docker-Compose 安装 + * `cd swoft` * `docker-compose up` @@ -96,6 +99,9 @@ AUTO_RELOAD=true HTTP_HOST=0.0.0.0 HTTP_PORT=80 +# WebSocket +WS_ENABLE_HTTP=true + # TCP TCP_HOST=0.0.0.0 TCP_PORT=8099 @@ -120,7 +126,7 @@ TASK_WORKER_NUM=1 **帮助命令** ``` -[root@swoft bin]# php swoft -h +[root@swoft]# php bin/swoft -h ____ __ _ / ___|_ _____ / _| |_ \___ \ \ /\ / / _ \| |_| __| @@ -128,16 +134,18 @@ TASK_WORKER_NUM=1 |____/ \_/\_/ \___/|_| \__| Usage: - php swoft -h + php bin/swoft -h Commands: - entity the group command list of database entity - rpc the group command list of rpc server - server the group command list of http-server + entity The group command list of database entity + gen Generate some common application template classes + rpc The group command list of rpc server + server The group command list of http-server + ws There some commands for manage the webSocket server Options: - -v,--version show version - -h,--help show help + -v, --version show version + -h, --help show help ``` **HTTP启动** @@ -162,7 +170,6 @@ php bin/swoft stop ``` - **RPC启动** > 启动独立的RPC服务器 From a4b7435d855b754ef0d578b80148d9f6acee1a3d Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Fri, 30 Mar 2018 02:41:43 +0800 Subject: [PATCH 192/643] Update .php_cs Use psr-2 rules --- .php_cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.php_cs b/.php_cs index 9ff88fcc..41e1b734 100644 --- a/.php_cs +++ b/.php_cs @@ -3,15 +3,16 @@ $header = <<<'EOF' This file is part of Swoft. -@link https://swoft.org +@link https://swoft.org @document https://doc.swoft.org -@contact group@swoft.org -@license https://github.com/swoft-cloud/swoft/blob/master/LICENSE +@contact group@swoft.org +@license https://github.com/swoft-cloud/swoft/blob/master/LICENSE EOF; return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setRules([ + '@PSR2' => true, 'header_comment' => [ 'commentType' => 'PHPDoc', 'header' => $header, @@ -21,6 +22,9 @@ return PhpCsFixer\Config::create() 'syntax' => 'short' ], 'single_quote' => true, + 'class_attributes_separation' => true, + 'no_unused_imports' => true, + 'standardize_not_equals' => true, ]) ->setFinder( PhpCsFixer\Finder::create() From 5ac20c82a3a0b92a2113cbfeeb3f6faf4796599b Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Fri, 30 Mar 2018 23:56:04 +0800 Subject: [PATCH 193/643] Update changelog.md --- changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.md b/changelog.md index 1f82c665..0159f240 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,6 @@ +# 2018-01-05 +* 组件化拆分 + # 2018-01-05 * 重构HttpClient * 重构Redis From 4a4de143e474eb037158023a3dc61197b1c786c5 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Fri, 30 Mar 2018 23:56:17 +0800 Subject: [PATCH 194/643] Update changelog.md --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 0159f240..d85aa9f9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,4 +1,4 @@ -# 2018-01-05 +# 2018-03-05 * 组件化拆分 # 2018-01-05 From ebcd9e1933b9de44811cb5fa0feb20ef928b454c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 1 Apr 2018 23:35:05 +0800 Subject: [PATCH 195/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aac7540b..cdd8d52f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

- Latest Version + Latest Version Build Status From 7fa7f4fbed76536f3cf5945e84e0641b828ec529 Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 2 Apr 2018 10:25:23 +0800 Subject: [PATCH 196/643] Update README.md --- README.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cdd8d52f..8ce09e41 100644 --- a/README.md +++ b/README.md @@ -121,11 +121,11 @@ LOG_FILE=@runtime/swoole.log TASK_WORKER_NUM=1 ``` -## 启动 +## 管理 -**帮助命令** +### 帮助命令 -``` +```text [root@swoft]# php bin/swoft -h ____ __ _ / ___|_ _____ / _| |_ @@ -134,7 +134,7 @@ TASK_WORKER_NUM=1 |____/ \_/\_/ \___/|_| \__| Usage: - php bin/swoft -h + php bin/swoft {command} [arguments ...] [options ...] Commands: entity The group command list of database entity @@ -148,11 +148,11 @@ Options: -h, --help show help ``` -**HTTP启动** +## HTTP Server启动 > 是否同时启动RPC服务器取决于.env文件配置 -```php +```bash // 启动服务,根据 .env 配置决定是否是守护进程 php bin/swoft start @@ -167,14 +167,34 @@ php bin/swoft reload // 关闭服务 php bin/swoft stop +``` + +### WebSocket Server启动 + +启动WebSocket服务器,可选是否同时支持http处理 +```bash +// 启动服务,根据 .env 配置决定是否是守护进程 +php bin/swoft ws:start + +// 守护进程启动,覆盖 .env 守护进程(DAEMONIZE)的配置 +php bin/swoft ws:start -d + +// 重启 +php bin/swoft ws:restart + +// 重新加载 +php bin/swoft ws:reload + +// 关闭服务 +php bin/swoft ws:stop ``` -**RPC启动** +## RPC Server启动 > 启动独立的RPC服务器 -```php +```bash // 启动服务,根据 .env 配置决定是否是守护进程 php bin/swoft rpc:start From 029f6e797e2818891b7a403736cb8658ab66a9ad Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 9 Apr 2018 18:00:11 +0800 Subject: [PATCH 197/643] Merge network server --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8ce09e41..0c8efe90 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,7 @@ 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 - 基于 Swoole 扩展 -- 内置协程网络服务器 -- webSocket 服务器 +- 内置协程 HTTP, TCP, WebSocket 网络服务器 - 强大的 AOP (面向切面编程) - 灵活完善的注解功能 - 全局的依赖注入容器 From 8497da0a47625848e52fe95a51f88f0dc1eadc70 Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Mon, 9 Apr 2018 18:10:23 +0800 Subject: [PATCH 198/643] Revert badge to markdown syntax --- README.md | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0c8efe90..c3841458 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,17 @@

-

- - Latest Version - - Build Status - - Php Version - - Swoole Version - - Hiredis Version - - Swoft Doc - - Swoft License -

+[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) +[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) +[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) + # 简介 -首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield, 有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 +首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield,有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 - 基于 Swoole 扩展 - 内置协程 HTTP, TCP, WebSocket 网络服务器 From 6c7e504337b641ec578e4db51fd38487dd38d672 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 10 Apr 2018 12:02:31 +0800 Subject: [PATCH 199/643] Update EchoController.php --- app/WebSocket/EchoController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/WebSocket/EchoController.php b/app/WebSocket/EchoController.php index 93204b1b..867903ce 100644 --- a/app/WebSocket/EchoController.php +++ b/app/WebSocket/EchoController.php @@ -49,6 +49,6 @@ public function onMessage(Server $server, Frame $frame) */ public function onClose(Server $server, int $fd) { - $server->push($fd, 'oo, goodbye! :)'); + // do something. eg. record log, unbind user ... } } From 91d405cb1cd4ef0f6f37860f273c9ae148fcb0c7 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 10 Apr 2018 12:06:39 +0800 Subject: [PATCH 200/643] Update changelog.md --- changelog.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/changelog.md b/changelog.md index d85aa9f9..84fb4b71 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,15 @@ +# Change log + +# 2018-04-05 + +* 添加 websocket 支持 +* 完成基本的 devtool + # 2018-03-05 * 组件化拆分 # 2018-01-05 + * 重构HttpClient * 重构Redis * 创建实体新增特殊变量别名 @@ -26,41 +34,67 @@ * 新增HTTP、RPC验证器 * 新增HTTP、RPC中间件 + # 2017-12-2 + * 新增RESTful风格兼容 + # 2017-11-29 + * 重构请求流程 * 简化控制器和RPC服务操作 * 增加 创建实体 操作 + # 2017-11-15 + * 增加 Pipeline 组件 + # 2017-11-13 + * 增加 PHPUnit 单元测试 + # 2017-11-12 + * 根据 Psr-7 重构 Request/Response + # 2017-11-02 + * 重构 config 配置 * 新增 .env 配置环境信息 + # 2017-11-01 + * 新增定时任务 + # 2017-10-24 + * 协程、异步任务投递 * 自定义用户进程 * RPC, Redis, Http, Mysql 协程和同步客户端无缝切换 * HTTP 和 RPC 服务器分开管理 + # 2017-09-19 + * 数据库 ORM + # 2017-09-02 + * 别名机制 * 事件机制 * 国际化(i18n) * 命名空间统一大写。 + # 2017-08-28 * 新增 Inotify 自动 Reload + # 2017-08-24 + * 重写 IoC 容器 * 新增控制器路由注解注册 * 重写容器注入,不再依赖 PHP-DI + # 2017-08-15 + * 重构 Console 命令行 + # ...... From f48a659350fc1622987a82e743cf68601a33725c Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Tue, 10 Apr 2018 21:53:21 +0800 Subject: [PATCH 201/643] refactor db --- app/Controllers/OrmController.php | 523 +++++++--------------------- app/Controllers/RedisController.php | 12 +- app/Controllers/RpcController.php | 6 + app/Tasks/SyncTask.php | 10 +- 4 files changed, 149 insertions(+), 402 deletions(-) diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 0fd52216..281cbc92 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -2,7 +2,7 @@ /** * This file is part of Swoft. * - * @link https://swoft.org + * @link https://swoft.org * @document https://doc.swoft.org * @contact group@swoft.org * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE @@ -12,520 +12,251 @@ use App\Models\Entity\Count; use App\Models\Entity\User; -use Swoft\Db\Pool; +use Swoft\Db\Db; +use Swoft\Db\Query; use Swoft\Http\Server\Bean\Annotation\Controller; -use Swoft\Db\EntityManager; -use Swoft\Db\QueryBuilder; -use Swoft\Db\Types; +use Swoft\Http\Server\Bean\Annotation\RequestMapping; /** * @Controller() */ class OrmController { - public function arSave() + public function save() { $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $deferUser = $user->save(); - $count = new Count(); - $count->setUid(999); - $count->setFans(mt_rand(1, 1000)); - $count->setFollows(mt_rand(1, 1000)); - $deferCount = $count->save(); + $userId = $user->save()->getResult(); - $userResult = $deferUser->getResult(); - $countResult = $deferCount->getResult(); + return [$userId]; + } - $user = new User(); - $user->setName('stelin2'); - $user->setSex(1); - $user->setDesc('this my desc2'); - $user->setAge(mt_rand(1, 100)); - $directUser = $user->save()->getResult(); + public function findById() + { + $result = User::findById(41710)->getResult(); + $query = User::findById(41710); - $count = new Count(); - $count->setUid($directUser); - $count->setFans(mt_rand(1, 1000)); - $count->setFollows(mt_rand(1, 1000)); - $directCount = $count->save()->getResult(); + /* @var User $user */ + $user = $query->getResult(User::class); - return [$userResult, $countResult, $directUser, $directCount]; + return [$result, $user->getName()]; } - public function test(){ - $sql = 'select * from user'; - $em = EntityManager::create(); - $result = $em->createQuery($sql)->execute()->getResult(); -// $em->close(); - - return [$result]; + public function selectDb(){ + $data = [ + 'name' => 'name', + 'sex' => 1, + 'description' => 'this my desc', + 'age' => mt_rand(1, 100), + ]; + $result = Query::table(User::class)->selectDb('test2')->insert($data)->getResult(); + return $result; } - public function notGetResult(){ - - $user2 = User::findById(4212)->getResult(); - $user = User::findById(4212); - return [$user2]; + public function selectTable(){ + $data = [ + 'name' => 'name', + 'sex' => 1, + 'description' => 'this my desc', + 'age' => mt_rand(1, 100), + ]; + $result = Query::table('user2')->insert($data)->getResult(); + return $result; } - /** - * @return array - */ - public function notCommitAndClose() + public function transactionCommit() { - $user2 = User::findById(4212)->getResult(); - + Db::beginTransaction(); $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $em = EntityManager::create(); - $em->beginTransaction(); - $result = $em->save($user)->getResult(); + $userId = $user->save()->getResult(); + Db::commit(); - return [$user2, $result]; + + return $userId; } - /** - * @return array - */ - public function notCommitAndCloseAndGetResult() + public function transactionRollback() { - $user2 = User::findById(4212)->getResult(); + Db::beginTransaction(); $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $em = EntityManager::create(); - $em->beginTransaction(); - $result = $em->save($user); + $userId = $user->save()->getResult(); - return [$user2, $result]; - } + $count = new Count(); + $count->setUid($userId); + $count->setFollows(mt_rand(1, 100)); + $count->setFans(mt_rand(1, 100)); - /** - * @return array - */ - public function notCommit() - { - $user2 = User::findById(4212)->getResult(); + $countId = $count->save()->getResult(); - $user = new User(); - $user->setName('stelin'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); + Db::rollback(); - $em = EntityManager::create(); - $em->beginTransaction(); - $result = $em->save($user)->getResult(); - $em->close(); - return [$user2, $result]; + return [$userId, $countId]; } /** - * @return array + * This is a wrong operation, only used to test + * + * @return mixed */ - public function notCloseButCommit() + public function transactionNotCommitOrRollback() { - $user2 = User::findById(4212)->getResult(); - + Db::beginTransaction(); $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $em = EntityManager::create(); - $em->beginTransaction(); - $result = $em->save($user)->getResult(); - $em->commit(); + $userId = $user->save()->getResult(); + + // This is a wrong operation, You must to commit or rollback + // ... - return [$user2, $result]; + return $userId; } /** - * @return array + * This is a wrong operation, only used to test + * + * @RequestMapping("tsnonr") + * @return mixed */ - public function closeAndNotGetResult() + public function transactionNotCommitOrRollbackAndNotGetResult() { + Db::beginTransaction(); $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $em = EntityManager::create(); - $user2 = User::findById(4212)->getResult(); - $em->beginTransaction(); - $result = $em->save($user)->getResult(); - $result = $em->save($user); - $em->commit(); - $em->close(); + $userId = $user->save(); - return [$user2, $result]; + // This is a wrong operation, You must to commit or rollback + // ... + + return ['11']; } /** - * EM查找 + * This is a wrong operation, only used to test + * + * @RequestMapping("tsng") + * @return mixed */ - public function save() + public function transactionNotGetResult() { + Db::beginTransaction(); $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $em = EntityManager::create(); - // $result = $em->save($user); - $result = $em->save($user)->getResult(); - $em->close(); + $userId = $user->save(); + Db::commit(); - return [$result]; - } - - public function forceMater() - { - $user = User::findById(4212, 'default.master')->getResult(); - return [$user]; - } - - /** - * 实体内容删除 - */ - public function arDelete() - { - $user = new User(); - // $user->setId(286); - $user->setAge(126); - - // $result = $user->delete(); - $defer = $user->delete(); - - return $defer->getResult(); + return [333]; } /** - * Em 删除 + * This is a wrong operation, only used to test + * + * @RequestMapping("tsng2") + * @return mixed */ - public function delete() + public function transactionNotGetResult2() { + Db::beginTransaction(); $user = new User(); - $user->setId(418); - - $em = EntityManager::create(); - // $result = $em->delete($user); - $result = $em->delete($user); - $em->close(); - - return [$result->getResult()]; - } - - /** - * EM deleteId - */ - public function deleteId() - { - $em = EntityManager::create(); - // $result = $em->deleteById(Count::class, 396); - $result = $em->deleteById(Count::class, 406); - $em->close(); - return [$result->getResult()]; - } - - /** - * EM DeleteIds - */ - public function deleteIds() - { - $em = EntityManager::create(); - // $result = $em->deleteByIds(Count::class, [409, 410]); - $result = $em->deleteByIds(Count::class, [411, 412]); - $em->close(); - return [$result->getResult()]; - } - - /** - * 删除ID测试 - */ - public function arDeleteId() - { - // $result = User::deleteById(284); - $result = User::deleteById(287); - - return $result->getResult(); - } + $user->setName('name'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); - /** - * 删除IDs测试 - */ - public function arDeleteIds() - { - // $result = User::deleteByIds([291, 292]); - $result = User::deleteByIds([288, 289]); + $userId = $user->save(); + Db::rollback(); - return $result->getResult(); + return [33]; } /** - * 更新操作 + * This is a wrong operation, only used to test + * + * @return mixed */ - public function arUpdate() + public function notGetResult() { - $query = User::findById(285); + $result = User::findById(19362); + $query = User::findById(19362); /* @var User $user */ $user = $query->getResult(User::class); - $user->setName('upateNameUser2'); - $user->setSex(0); - - $result = $user->update(); - // $result = $user->update(true); - // $result = $result->getResult(); - - return [$result->getResult()]; - } - /** - * 实体查找 - */ - public function arFind() - { - $user = new User(); - $user->setSex(1); - $user->setAge(93); - $query = $user->find(); - - $result = $query->getResult(User::class); - return [$result]; + return [33]; } /** - * EM find + * This is a wrong operation, only used to test + * + * @return mixed */ - public function find() + public function notGetResult2() { $user = new User(); + $user->setName('name'); $user->setSex(1); - $em = EntityManager::create(); - $query = $em->find($user); - // $result = $query->getResult(); - // $result = $query->getResult(User::class); - // $result = $query->getDefer()->getResult(); - $result = $query->getResult(User::class); - $em->close(); - - return [$result]; - } + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); - /** - * Ar ID查找 - */ - public function arFindId() - { - $result = User::findById(4212)->getResult(); + $userId = $user->save()->getResult(); - $query = User::findById(4212); + $result = User::findById(19362); + $query = User::findById(19362); /* @var User $user */ $user = $query->getResult(User::class); - return [$result, $user->getName()]; - } - - /** - * EM find id - */ - public function findId() - { - $em = EntityManager::create(); - $query = $em->findById(User::class, 396); - // $result = $query->getResult(); - // $result = $query->getResult(User::class); - $result = $query->getResult(); - $em->close(); - - return [$result]; - } - - /** - * Ar IDS查找 - */ - public function arFindIds() - { - $query = User::findByIds([416, 417]); - // $defer = $query->getDefer(); - // $result = $defer->getResult(User::class); - - $result = $query->getResult(); - - return [$result]; - } - - /** - * EM find ids - */ - public function findIds() - { - $em = EntityManager::create(); - $query = $em->findByIds(User::class, [396, 403]); - $result = $query->getResult(); - // $result = $query->getResult(User::class); - // $result = $query->getDefer()->getResult(User::class); - $em->close(); - - return [$result]; - } - - /** - * Ar Query - */ - public function arQuery() - { - // $query = User::query()->select('*')->andWhere('sex', 1)->orderBy('id',QueryBuilder::ORDER_BY_DESC)->limit(3); - // $query = User::query()->selects(['id', 'sex' => 'sex2'])->andWhere('sex', 1)->orderBy('id',QueryBuilder::ORDER_BY_DESC)->limit(3); - $query = User::query()->selects(['id', 'sex' => 'sex2'])->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 429) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); - // $result = $query->getResult(); - $result = $query->getResult(); - return [$result]; + return [222]; } /** - * EM 事务测试 + * This is a wrong operation, only used to test + * + * @return mixed */ - public function ts() + public function notGetResult3() { $user = new User(); - $user->setName('stelin'); + $user->setName('name'); $user->setSex(1); $user->setDesc('this my desc'); $user->setAge(mt_rand(1, 100)); - $user2 = new User(); - $user2->setName('stelin'); - $user2->setSex(1); - $user2->setDesc('this my desc'); - $user2->setAge(mt_rand(1, 100)); - - $count = new Count(); - $count->setFans(mt_rand(1, 1000)); - $count->setFollows(mt_rand(1, 1000)); - - $em = EntityManager::create(); - $re = $user2->save()->getResult(); - $em->beginTransaction(); - - $uid = $em->save($user)->getResult(); - $count->setUid($uid); + $userId = $user->save(); - $result = $em->save($count)->getResult(); + $result = User::findById(19362); + $query = User::findById(19362); - $result2 = $user2->save()->getResult(); - $em->rollback(); -// $em->commit(); - $em->close(); - - return [$uid, $result]; - } - - public function query() - { - $em = EntityManager::create(); - $query = $em->createQuery(); - $result = $query->select('*')->from(User::class, 'u')->leftJoin(Count::class, ['u.id=c.uid'], 'c')->whereIn('u.id', [419, 420, 421]) - ->orderBy('u.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); - // $result = $query->getResult(); - $result = $result->getResult(); - $em->close(); - - return [$result]; - } - - /** - * 并发执行两个语句 - */ - public function arCon() - { - $query1 = User::query()->selects(['id', 'sex' => 'sex2'])->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 419) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); - - $query2 = User::query()->select('*')->leftJoin(Count::class, 'count.uid=user.id')->andWhere('id', 420) - ->orderBy('user.id', QueryBuilder::ORDER_BY_DESC)->limit(2)->execute(); - - $result1 = $query1->getResult(); - $result2 = $query2->getResult(); - return [$result1, $result2]; - } + /* @var User $user */ + $user = $query->getResult(User::class); - public function sql() - { - $ids = [4212, 4213]; - $poolId = Pool::MASTER; - - $em = EntityManager::create($poolId); - $result = $em->createQuery('select * from user where id in(?, ?) and name = ? order by id desc limit 2') - ->setParameter(0, $ids[0]) - ->setParameter(1, $ids[1]) - ->setParameter(2, 'stelin') - ->execute()->getResult(); - $em->close(); - - $em = EntityManager::create($poolId); - $result2 = $em->createQuery('select * from user where id in(?, ?) and name = ? order by id desc limit 2') - ->setParameter(0, $ids[0]) - ->setParameter(1, $ids[1]) - ->setParameter(2, 'stelin', Types::STRING) - ->execute()->getResult(); - $em->close(); - - $em = EntityManager::create($poolId); - $result3 = $em->createQuery('select * from user where id in(?, ?) and name = ? order by id desc limit 2') - ->setParameters([$ids[0], $ids[1], 'stelin']) - ->execute()->getResult(); - $em->close(); - - $em = EntityManager::create($poolId); - $result4 = $em->createQuery('select * from user where id in(:id1, :id2) and name = :name order by id desc limit 2') - ->setParameter(':id1', $ids[0]) - ->setParameter('id2', $ids[1]) - ->setParameter('name', 'stelin') - ->execute()->getResult(); - $em->close(); - - $em = EntityManager::create($poolId); - $result5 = $em->createQuery('select * from user where id in(:id1, :id2) and name = :name order by id desc limit 2') - ->setParameters([ - 'id1' => $ids[0], - ':id2' => $ids[1], - 'name' => 'stelin' - ]) - ->execute()->getResult(); - $em->close(); - - - $em = EntityManager::create($poolId); - $result6 = $em->createQuery('select * from user where id in(:id1, :id2) and name = :name order by id desc limit 2') - ->setParameters([ - ['id1', $ids[0]], - [':id2', $ids[1], Types::INT], - ['name', 'stelin', Types::STRING], - ]) - ->execute()->getResult(); - $em->close(); - - return [\count($result)]; + return [33]; } } \ No newline at end of file diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php index 293c1e10..f92ac9fa 100644 --- a/app/Controllers/RedisController.php +++ b/app/Controllers/RedisController.php @@ -81,7 +81,7 @@ public function error() public function ab() { - $result1 = User::query()->select('*')->where('id', '720')->limit(1)->execute()->getResult(); + $result1 = User::query()->where('id', '720')->limit(1)->get()->getResult(); $result2 = $this->redis->set('test1', 1); return [$result1, $result2]; @@ -156,15 +156,21 @@ public function has() public function testDefer() { $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); - $ret2 = $this->redis->deferCall('set', ['name2', 'swoft2']); +// $ret2 = $this->redis->deferCall('set', ['name2', 'swoft2']); $r1 = $ret1->getResult(); $r2 = 1; - $r2 = $ret2->getResult(); +// $r2 = $ret2->getResult(); $ary = 1; // $ary = $this->redis->getMultiple(['name1', 'name2']); return [$r1, $r2, $ary]; } + + public function deferError() + { + $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); + return 'error'; + } } \ No newline at end of file diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php index f98a391c..f2f20905 100644 --- a/app/Controllers/RpcController.php +++ b/app/Controllers/RpcController.php @@ -114,6 +114,12 @@ public function defer(){ return [$result1, $result2, $result3]; } + public function deferError() + { + $defer1 = $this->demoService->deferGetUser('123'); + return ['error']; + } + public function beanCall() { return [ diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index ec68852e..4383c745 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -15,6 +15,7 @@ use Swoft\App; use Swoft\Bean\Annotation\Inject; use Swoft\HttpClient\Client; +use Swoft\Redis\Redis; use Swoft\Rpc\Client\Bean\Annotation\Reference; use Swoft\Task\Bean\Annotation\Scheduled; use Swoft\Task\Bean\Annotation\Task; @@ -84,9 +85,12 @@ public function deliverAsync(string $p1, string $p2) */ public function cache() { - cache()->set('cacheKey', 'cache'); - - return cache('cacheKey'); + /* @var Redis $cache */ + $cache = \Swoft\App::getBean(Redis::class); +// $ret1 = $cache->deferCall('set', ['name1', 'swoft1'])->getResult(); + $ret1 = $cache->deferCall('set', ['name1', 'swoft1']); +// return cache('cacheKey'); + return 111; } /** From d8ff4f26bfd2361a53cd2ada12b8bc05a65b0c60 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Wed, 11 Apr 2018 14:35:59 +0800 Subject: [PATCH 202/643] Update Dockerfile Upgrade swoole to v2.1.2 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c692d568..b5c53b45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.1.1.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.1.2.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From c14c08dc9f9b564046d9994a3b62364b76cddfb6 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 13 Apr 2018 11:21:34 +0800 Subject: [PATCH 203/643] add dev composer.json --- dev.composer.json | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 dev.composer.json diff --git a/dev.composer.json b/dev.composer.json new file mode 100644 index 00000000..984e79d2 --- /dev/null +++ b/dev.composer.json @@ -0,0 +1,68 @@ +{ + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", + "license": "Apache-2.0", + "require": { + "php": ">=7.0", + "swoft/framework": "^1.0", + "swoft/rpc": "^1.0", + "swoft/rpc-server": "^1.0", + "swoft/rpc-client": "^1.0", + "swoft/http-server": "^1.0", + "swoft/http-client": "^1.0", + "swoft/websocket-server": "^1.0", + "swoft/task": "^1.0", + "swoft/http-message": "^1.0", + "swoft/view": "^1.0", + "swoft/db": "^1.0", + "swoft/cache": "^1.0", + "swoft/redis": "^1.0", + "swoft/console": "^1.0", + "swoft/devtool": "^1.0", + "swoft/session": "^1.0", + "swoft/i18n": "^1.0", + "swoft/process": "^1.0", + "swoft/memory": "^1.0", + "swoft/service-governance": "^1.0", + "swoft/component": "dev-master as 1.0" + }, + "require-dev": { + "eaglewu/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^5.7" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Swoft.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Swoft\\Test\\": "test/" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml" + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-component" + }, + { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" + } + ] +} From b25db1d4fcd7221ae55a1d727ba68404af3c33f5 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Sun, 15 Apr 2018 02:09:12 +0800 Subject: [PATCH 204/643] Update Dockerfile Upgrade swoole to v2.1.3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b5c53b45..01b3602e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.1.2.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.1.3.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From 169ce33d5558ccf0a33a8010782228516fcbfb4e Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Sun, 15 Apr 2018 03:33:43 +0800 Subject: [PATCH 205/643] Add English version README.md --- README-EN.md | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 README-EN.md diff --git a/README-EN.md b/README-EN.md new file mode 100644 index 00000000..7fd35c44 --- /dev/null +++ b/README-EN.md @@ -0,0 +1,210 @@ +

+     + swoft + +

+ +[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) +[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) +[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) + + +# Introduction +The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, standard PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. + +- Base on Swoole extension +- Built-in HTTP, TCP, WebSocket Server +- Poweful AOP (Aspect Oriented Programming) +- Flexible and comprehensive annotations framework +- Global dependency injection container +- PSR-7 based HTTP message implementation +- PSR-14 based event manager +- PSR-15 based middleware +- PSR-16 based cache design +- Scalable high performance RPC +- Great service governance, fallback, load balance, service registration and discovery +- Database ORM +- Universal connection pool +- Mysql, Redis, RPC, HTTP Coroutine Clients +- Coroutine driver client and blocking driver client seamlessly switch automatically +- Coroutine and asynchronous task delivery +- Custom user process +- RESTful support +- Internationalization (i18n) support +- High performance router +- Fast and flexible parameter validator +- Alias mechanism +- Powerful log component +- Cross-platform application auto-reload + + +# Document +[**Chinese Document**](https://doc.swoft.org) +[**English Document**](https://doc.swoft.org) Not yet, please help us write it. + +QQ Group: 548173319 + +# Environmental Requirements + +1. PHP 7.0 + +2. [Swoole 2.1.1](https://github.com/swoole/swoole-src/releases) +, *coroutine* and *async redis client* options are required +3. [Hiredis](https://github.com/redis/hiredis/releases) +4. [Composer](https://getcomposer.org/) + +# Install + +## Manual Installation + +* Clone project +* Install requires `composer install` + +## Install by Composer + +* `composer create-project swoft/swoft swoft` + +## Install by Docker + +* `docker run -p 80:80 swoft/swoft` + +## Install by Docker-Compose + +* `cd swoft` +* `docker-compose up` + +# Configuration + +If automatically copy `.env` file fails when `composer install` is executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that when `composer update` is executed will not trigger related copy operations + +``` +# Server +PFILE=/tmp/swoft.pid +PNAME=php-swoft +TCPABLE=true +CRONABLE=false +AUTO_RELOAD=true + +# HTTP +HTTP_HOST=0.0.0.0 +HTTP_PORT=80 + +# WebSocket +WS_ENABLE_HTTP=true + +# TCP +TCP_HOST=0.0.0.0 +TCP_PORT=8099 +TCP_PACKAGE_MAX_LENGTH=2048 +TCP_OPEN_EOF_CHECK=false + +# Crontab +CRONTAB_TASK_COUNT=1024 +CRONTAB_TASK_QUEUE=2048 + +# Settings +WORKER_NUM=1 +MAX_REQUEST=10000 +DAEMONIZE=0 +DISPATCH_MODE=2 +LOG_FILE=@runtime/swoole.log +TASK_WORKER_NUM=1 +``` + +## Management + +### Help command + +```text +[root@swoft]# php bin/swoft -h + ____ __ _ +/ ___|_ _____ / _| |_ +\___ \ \ /\ / / _ \| |_| __| + ___) \ V V / (_) | _| |_ +|____/ \_/\_/ \___/|_| \__| + +Usage: + php bin/swoft {command} [arguments ...] [options ...] + +Commands: + entity The group command list of database entity + gen Generate some common application template classes + rpc The group command list of rpc server + server The group command list of http-server + ws There some commands for manage the webSocket server + +Options: + -v, --version show version + -h, --help show help +``` + +## Start HTTP Server + +```bash +// Start HTTP Server +php bin/swoft start + +// Start Daemonize HTTP Server +php bin/swoft start -d + +// Restart HTTP server +php bin/swoft restart + +// Reload HTTP server +php bin/swoft reload + +// Stop HTTP server +php bin/swoft stop +``` + +### Start WebSocket Server + +Start WebSocket Server, optional whether to support http processing + +```bash +// Star WebSocket Server +php bin/swoft ws:start + +// Start Daemonize WebSocket Server +php bin/swoft ws:start -d + +// Restart WebSocket server +php bin/swoft ws:restart + +// Reload WebSocket server +php bin/swoft ws:reload + +// Stop WebSocket server +php bin/swoft ws:stop +``` + +## Start RPC Server + +> Start an independent RPC Server + +```bash +// Start RPC Server +php bin/swoft rpc:start + +// Start Daemonize RPC Server +php bin/swoft rpc:start -d + +// Restart RPC Server +php bin/swoft rpc:restart + +// Reload RPC Server +php bin/swoft rpc:reload + +// Stop RPC Server +php bin/swoft rpc:stop + +``` + +# Changelog + +[Changelog](changelog.md) + +# License +Swoft is open-source software licensed under the [LICENSE](LICENSE) From d64161f050f0486594e38c42a6cd53db4685a97c Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Sun, 15 Apr 2018 03:34:11 +0800 Subject: [PATCH 206/643] Update Swoole extension version requires --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3841458..a7857619 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) From ac24070606efc99b0958ad3f3500a6e952f3305d Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Sun, 15 Apr 2018 03:36:35 +0800 Subject: [PATCH 207/643] Update README-EN.md --- README-EN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-EN.md b/README-EN.md index 7fd35c44..b4e0008f 100644 --- a/README-EN.md +++ b/README-EN.md @@ -14,7 +14,7 @@ # Introduction -The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, standard PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. +The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, standard PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. - Base on Swoole extension - Built-in HTTP, TCP, WebSocket Server From 32c63312a05b8a07e17eb86f368427e11e59ac6f Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Apr 2018 09:17:56 +0800 Subject: [PATCH 208/643] Update README-EN.md --- README-EN.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README-EN.md b/README-EN.md index b4e0008f..4574ff03 100644 --- a/README-EN.md +++ b/README-EN.md @@ -12,8 +12,8 @@ [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +## Introduction -# Introduction The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, standard PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. - Base on Swoole extension @@ -42,40 +42,41 @@ The first high-performance PHP coroutine full-stack componentization framework b - Cross-platform application auto-reload -# Document +## Document + [**Chinese Document**](https://doc.swoft.org) [**English Document**](https://doc.swoft.org) Not yet, please help us write it. QQ Group: 548173319 -# Environmental Requirements +## Environmental Requirements 1. PHP 7.0 + 2. [Swoole 2.1.1](https://github.com/swoole/swoole-src/releases) +, *coroutine* and *async redis client* options are required 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) -# Install +## Install -## Manual Installation +### Manual Installation * Clone project * Install requires `composer install` -## Install by Composer +### Install by Composer * `composer create-project swoft/swoft swoft` -## Install by Docker +### Install by Docker * `docker run -p 80:80 swoft/swoft` -## Install by Docker-Compose +### Install by Docker-Compose * `cd swoft` * `docker-compose up` -# Configuration +## Configuration If automatically copy `.env` file fails when `composer install` is executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that when `composer update` is executed will not trigger related copy operations @@ -140,7 +141,7 @@ Options: -h, --help show help ``` -## Start HTTP Server +### Start HTTP Server ```bash // Start HTTP Server @@ -180,7 +181,7 @@ php bin/swoft ws:reload php bin/swoft ws:stop ``` -## Start RPC Server +### Start RPC Server > Start an independent RPC Server @@ -199,12 +200,12 @@ php bin/swoft rpc:reload // Stop RPC Server php bin/swoft rpc:stop - ``` -# Changelog +## Changelog [Changelog](changelog.md) -# License +## License + Swoft is open-source software licensed under the [LICENSE](LICENSE) From 12e0685191d560214eca2bb64a3ddc39d3d22d62 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Apr 2018 09:19:45 +0800 Subject: [PATCH 209/643] Update README.md --- README.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a7857619..3ca8fc33 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +## 简介 -# 简介 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield,有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 - 基于 Swoole 扩展 @@ -42,39 +42,40 @@ - 跨平台热更新自动 Reload -# 文档 +## 文档 + [**中文文档**](https://doc.swoft.org) QQ 交流群: 548173319 -# 环境要求 +## 环境要求 1. PHP 7.0 + 2. [Swoole 2.1.1](https://github.com/swoole/swoole-src/releases) +, 需开启协程和异步Redis 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) -# 安装 +## 安装 -## 手动安装 +### 手动安装 * Clone 项目 * 安装依赖 `composer install` -## Composer 安装 +### Composer 安装 * `composer create-project swoft/swoft swoft` -## Docker 安装 +### Docker 安装 * `docker run -p 80:80 swoft/swoft` -## Docker-Compose 安装 +### Docker-Compose 安装 * `cd swoft` * `docker-compose up` -# 配置 +## 配置 若在执行 `composer install` 的时候由程序自动复制环境变量配置文件失败,则可手动复制项目根目录的 `.env.example` 并命名为 `.env`,注意在执行 `composer update` 时并不会触发相关的复制操作 @@ -139,7 +140,7 @@ Options: -h, --help show help ``` -## HTTP Server启动 +### HTTP Server启动 > 是否同时启动RPC服务器取决于.env文件配置 @@ -181,7 +182,7 @@ php bin/swoft ws:reload php bin/swoft ws:stop ``` -## RPC Server启动 +### RPC Server启动 > 启动独立的RPC服务器 @@ -200,12 +201,12 @@ php bin/swoft rpc:reload // 关闭服务 php bin/swoft rpc:stop - ``` -# 更新日志 +## 更新日志 [更新日志](changelog.md) -# 协议 +## 协议 + Swoft 的开源协议为 Apache-2.0,详情参见[LICENSE](LICENSE) From 37af822a1f6071d70329404d9d30596fbe1e64fb Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Apr 2018 10:43:46 +0800 Subject: [PATCH 210/643] add timezone --- .env.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.env.example b/.env.example index 3a69ebb9..452e9b0d 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ +# Application +TIME_ZONE=Asia/Shanghai + # Server PFILE=/tmp/swoft.pid PNAME=php-swoft From 02d51dcc38d918af0ba71364808eb60c15caf74b Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Apr 2018 11:10:03 +0800 Subject: [PATCH 211/643] Update and rename README.md to README_CN.md --- README.md => README_CN.md | 2 ++ 1 file changed, 2 insertions(+) rename README.md => README_CN.md (99%) diff --git a/README.md b/README_CN.md similarity index 99% rename from README.md rename to README_CN.md index 3ca8fc33..31f7565d 100644 --- a/README.md +++ b/README_CN.md @@ -12,6 +12,8 @@ [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +**[English](README.md)** + ## 简介 首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield,有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 From 0dc576c2efa6873f56dd1d17d420895d7655d376 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Apr 2018 11:10:57 +0800 Subject: [PATCH 212/643] Update and rename README-EN.md to README.md --- README-EN.md => README.md | 2 ++ 1 file changed, 2 insertions(+) rename README-EN.md => README.md (99%) diff --git a/README-EN.md b/README.md similarity index 99% rename from README-EN.md rename to README.md index 4574ff03..8261f97b 100644 --- a/README-EN.md +++ b/README.md @@ -12,6 +12,8 @@ [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +**[中文说明](README_CN.md)** + ## Introduction The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, standard PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. From 39570750ed97693326d5ba408cf0aa01d75029ef Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Sun, 15 Apr 2018 17:59:49 +0800 Subject: [PATCH 213/643] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8261f97b..bb56481e 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ QQ Group: 548173319 ## Configuration -If automatically copy `.env` file fails when `composer install` is executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that when `composer update` is executed will not trigger related copy operations +If automatically copied `.env` file fails when `composer install` was executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that `composer update` will not trigger related copy operations. ``` # Server @@ -164,7 +164,7 @@ php bin/swoft stop ### Start WebSocket Server -Start WebSocket Server, optional whether to support http processing +Start WebSocket Server, optional whether to support HTTP processing. ```bash // Star WebSocket Server @@ -185,7 +185,7 @@ php bin/swoft ws:stop ### Start RPC Server -> Start an independent RPC Server +Start an independent RPC Server. ```bash // Start RPC Server @@ -210,4 +210,4 @@ php bin/swoft rpc:stop ## License -Swoft is open-source software licensed under the [LICENSE](LICENSE) +Swoft is an open-source software licensed under the [LICENSE](LICENSE) From 22791b8e4e48e22ca8769610d20d07df8c204917 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Tue, 17 Apr 2018 19:17:40 +0800 Subject: [PATCH 214/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb56481e..29f11c95 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ QQ Group: 548173319 ## Environmental Requirements 1. PHP 7.0 + -2. [Swoole 2.1.1](https://github.com/swoole/swoole-src/releases) +, *coroutine* and *async redis client* options are required +2. [Swoole 2.1.3](https://github.com/swoole/swoole-src/releases) +, *coroutine* and *async redis client* options are required 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) From 557d1e0f32927dfa2cf9f29d3354f88f6231e0bb Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Tue, 17 Apr 2018 19:18:01 +0800 Subject: [PATCH 215/643] Update README_CN.md --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 31f7565d..99bf2d1c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -53,7 +53,7 @@ QQ 交流群: 548173319 ## 环境要求 1. PHP 7.0 + -2. [Swoole 2.1.1](https://github.com/swoole/swoole-src/releases) +, 需开启协程和异步Redis +2. [Swoole 2.1.3](https://github.com/swoole/swoole-src/releases) +, 需开启协程和异步Redis 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) From b81c7822973a9329304084290ca7718ae5f90780 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 18 Apr 2018 10:49:34 +0800 Subject: [PATCH 216/643] add LOG_ENABLE in .env.example --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index 452e9b0d..233868b6 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ # Application TIME_ZONE=Asia/Shanghai +LOG_ENABLE=false # Server PFILE=/tmp/swoft.pid From bfb84a3ca8fd6fb49124efde3f055d3c095e6eb0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 18 Apr 2018 10:53:53 +0800 Subject: [PATCH 217/643] can enable logging through .env --- config/beans/log.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/beans/log.php b/config/beans/log.php index 732e5e06..06cac847 100644 --- a/config/beans/log.php +++ b/config/beans/log.php @@ -1,5 +1,4 @@ @@ -30,7 +29,7 @@ ], 'logger' => [ 'name' => APP_NAME, - 'enable' => false, + 'enable' => env('LOG_ENABLE', false), 'flushInterval' => 100, 'flushRequest' => true, 'handlers' => [ From 5338f0d9719c21ee228dadfcd35780882a36be89 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 19 Apr 2018 09:47:08 +0800 Subject: [PATCH 218/643] Create CONTRIBUTING.md --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..854139a3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1 @@ +# Contributing From 3a18191104b4c56f0f5ba629ab7bb5ff3e2858cc Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Thu, 19 Apr 2018 12:33:13 +0800 Subject: [PATCH 219/643] add CODE_OF_CONDUCT.md --- .github/CODE_OF_CONDUCT.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/CODE_OF_CONDUCT.md diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..c2a23273 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,12 @@ +Contributor Code of Conduct +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the Contributor Covenant, version 1.0.0, available at http://contributor-covenant.org/version/1/0/0/ \ No newline at end of file From ff8ef812d7265f82c33d28b6056fb4246773606b Mon Sep 17 00:00:00 2001 From: huangzhhui Date: Thu, 19 Apr 2018 12:33:22 +0800 Subject: [PATCH 220/643] add CONTRIBUTING.md --- CONTRIBUTING.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 854139a3..432fa5e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1,46 @@ -# Contributing +# Swoft Contributing Guide + +Hi! I am really excited that you are interested in contributing to Swoft. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. + +- [Code of Conduct](./.github/CODE_OF_CONDUCT.md) +- [Issue Reporting Guidelines](#issue-reporting-guidelines) +- [Pull Request Guidelines](#pull-request-guidelines) +- [Development Guidelines](#development-guidelines) + +## Issue Reporting Guidelines + +- Should always create an new issues by Github issue template, to avoid missing information. + +## Pull Request Guidelines + +The master branch is the latest stable release, the feature branch commonly is the next feature upgrade version. If it's a feature develoment, should be done in feature branch, if it's a bug-fix develoment, then you could done in master branch, or feature branch, the different between master branch an feature branch is that the feature branch will merge to master branch until next version upgraded, and master branch could release a bug-fix version anytime. + +Note that Swoft using [swoft-component](https://github.com/swoft-cloud/swoft-component) repository to centralized manage all Swoft components, if you want to submit an PR for component of swoft, then you should submit your PR to [swoft-component](https://github.com/swoft-cloud/swoft-component) repository. + +Checkout a topic branch from the relevant branch, e.g. feature, and merge back against that branch. + +It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging. + +Make sure unit test passes, commonly you could use `composer test` to run all unit testes. (see development setup) + +If adding new feature: + +Add accompanying test case. +Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it. +If fixing a bug: + +If you are resolving a special issue, add (fix #xxxx[,#xxx]) (#xxxx is the issue id) in your PR title for a better release log, e.g. update entities encoding/decoding (fix #3899). +Provide detailed description of the bug in the PR. Live demo preferred. +Add appropriate test coverage if applicable. + +## Development Guidelines + +Because Swoft using [swoft-component](https://github.com/swoft-cloud/swoft-component) repository to centralized manage all Swoft components, then you should add `swoft/component` requires to `composer.json` if you are developing in swoft forked repository, after this, component of swoft-component will replace all original components requires, see [Composer replace schema](https://getcomposer.org/doc/04-schema.md#replace) for more details. + +composer requires e.g. + +```json +"require": { + "swoft/component": "dev-master as 1.0" +}, +``` \ No newline at end of file From dd2c2dcddfa9fc018bfe650c0b970d06af35d784 Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Thu, 19 Apr 2018 12:35:06 +0800 Subject: [PATCH 221/643] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 432fa5e0..1c77a503 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,7 @@ Add appropriate test coverage if applicable. ## Development Guidelines -Because Swoft using [swoft-component](https://github.com/swoft-cloud/swoft-component) repository to centralized manage all Swoft components, then you should add `swoft/component` requires to `composer.json` if you are developing in swoft forked repository, after this, component of swoft-component will replace all original components requires, see [Composer replace schema](https://getcomposer.org/doc/04-schema.md#replace) for more details. +Because Swoft using [swoft-component](https://github.com/swoft-cloud/swoft-component) repository to centralized manage all Swoft components, then you should add `swoft/component` requires to `composer.json` if you are developing in swoft forked repository, after this, components of swoft-component will replace all original components requires, see [Composer replace schema](https://getcomposer.org/doc/04-schema.md#replace) for more details. composer requires e.g. @@ -43,4 +43,4 @@ composer requires e.g. "require": { "swoft/component": "dev-master as 1.0" }, -``` \ No newline at end of file +``` From 1e718330ca9d778002f28d14df8e93c6ae7b2f2f Mon Sep 17 00:00:00 2001 From: Huangzhhui Date: Sun, 22 Apr 2018 01:11:11 +0800 Subject: [PATCH 222/643] Update README.md Remove useless word --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 29f11c95..26aae8c7 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ## Introduction -The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, standard PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. +The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. - Base on Swoole extension - Built-in HTTP, TCP, WebSocket Server From b3004dc3cd33cb59c9ff805d20795c66313f371e Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 22 Apr 2018 23:32:00 +0800 Subject: [PATCH 223/643] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a714a299..ab30c4bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ php: install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - - pecl install -f swoole-2.0.12 + - pecl install -f swoole-2.1.3 before_script: - composer update --dev From 68d2263e39bc3e0e16f3ea1e7b327a001418a9a6 Mon Sep 17 00:00:00 2001 From: twosee Date: Wed, 25 Apr 2018 10:14:57 +0800 Subject: [PATCH 224/643] Fix the pecl installation. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ab30c4bf..e54460c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ php: install: - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - - pecl install -f swoole-2.1.3 + - printf "\n" | pecl install -f swoole-2.1.3 before_script: - composer update --dev From eafe92c32ae84d253f191cf39e75ad6b8cd29135 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 27 Apr 2018 14:35:46 +0800 Subject: [PATCH 225/643] Set task mode --- .env.example | 2 +- app/Controllers/DemoController.php | 14 -------------- app/Controllers/TaskController.php | 16 ++++++++++++++++ app/Tasks/SyncTask.php | 5 +++++ composer.json | 13 +++++++++---- config/beans/log.php | 2 +- config/server.php | 2 +- 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index 3a69ebb9..7b072e83 100644 --- a/.env.example +++ b/.env.example @@ -32,7 +32,7 @@ WORKER_NUM=1 MAX_REQUEST=100000 DAEMONIZE=0 DISPATCH_MODE=2 -TASK_IPC_MODE=2 +TASK_IPC_MODE=1 MESSAGE_QUEUE_KEY=1879052289 TASK_TMPDIR=/tmp/ LOG_FILE=@runtime/logs/swoole.log diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index fb4f86bf..6aef0fc6 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -99,20 +99,6 @@ public function index2() return 'success'; } - /** - * 没有使用注解,自动解析注入,默认支持get和post - */ - public function task() - { - $result = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_CO); - $mysql = Task::deliver('test', 'testMysql', [], Task::TYPE_CO); - $http = Task::deliver('test', 'testHttp', [], Task::TYPE_CO, 20); - $rpc = Task::deliver('test', 'testRpc', [], Task::TYPE_CO, 5); - $result1 = Task::deliver('test', 'asyncTask', [], Task::TYPE_ASYNC); - - return [$rpc, $http, $mysql, $result, $result1]; - } - public function index6() { throw new Exception('AAAA'); diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php index 0fc61779..e8d1c982 100644 --- a/app/Controllers/TaskController.php +++ b/app/Controllers/TaskController.php @@ -18,6 +18,22 @@ */ class TaskController { + /** + * @return array + */ + public function batch() + { + $count = 0; + $result = []; + while ($count < 10000){ + $result[] = Task::deliver('sync', 'batchTask', [], Task::TYPE_ASYNC); + $count++; + } + + return $result; + } + + /** * Deliver co task * diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index 4383c745..8e187c7a 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -156,6 +156,11 @@ public function rpc2() return $this->logic->rpcCall(); } + public function batchTask(){ + sleep(mt_rand(1, 2)); + return 'batchTask'; + } + /** * crontab定时任务 * 每一秒执行一次 diff --git a/composer.json b/composer.json index c73590b8..984e79d2 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,8 @@ "swoft/i18n": "^1.0", "swoft/process": "^1.0", "swoft/memory": "^1.0", - "swoft/service-governance": "^1.0" + "swoft/service-governance": "^1.0", + "swoft/component": "dev-master as 1.0" }, "require-dev": { "eaglewu/swoole-ide-helper": "dev-master", @@ -54,10 +55,14 @@ ], "test": "./vendor/bin/phpunit -c phpunit.xml" }, - "repositories": { - "packagist": { + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-component" + }, + { "type": "composer", "url": "https://packagist.phpcomposer.com/" } - } + ] } diff --git a/config/beans/log.php b/config/beans/log.php index 732e5e06..33ba6e33 100644 --- a/config/beans/log.php +++ b/config/beans/log.php @@ -30,7 +30,7 @@ ], 'logger' => [ 'name' => APP_NAME, - 'enable' => false, + 'enable' => true, 'flushInterval' => 100, 'flushRequest' => true, 'handlers' => [ diff --git a/config/server.php b/config/server.php index 8fab6ea3..c0dd2b19 100644 --- a/config/server.php +++ b/config/server.php @@ -53,7 +53,7 @@ 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), 'ssl_cert_file' => env('SSL_CERT_FILE', ''), 'ssl_key_file' => env('SSL_KEY_FILE', ''), - 'task_ipc_mode' => env('TASK_IPC_MODE', 2), + 'task_ipc_mode' => env('TASK_IPC_MODE', 1), 'message_queue_key' => env('MESSAGE_QUEUE_KEY', 0x70001001), 'task_tmpdir' => env('TASK_TMPDIR', '/tmp'), ], From 405599fdf48205bf465dfd5d9a3dc33468c31caa Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 27 Apr 2018 14:42:10 +0800 Subject: [PATCH 226/643] reset composer.json --- composer.json | 123 ++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/composer.json b/composer.json index 984e79d2..80daea5c 100644 --- a/composer.json +++ b/composer.json @@ -1,68 +1,63 @@ { - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", - "license": "Apache-2.0", - "require": { - "php": ">=7.0", - "swoft/framework": "^1.0", - "swoft/rpc": "^1.0", - "swoft/rpc-server": "^1.0", - "swoft/rpc-client": "^1.0", - "swoft/http-server": "^1.0", - "swoft/http-client": "^1.0", - "swoft/websocket-server": "^1.0", - "swoft/task": "^1.0", - "swoft/http-message": "^1.0", - "swoft/view": "^1.0", - "swoft/db": "^1.0", - "swoft/cache": "^1.0", - "swoft/redis": "^1.0", - "swoft/console": "^1.0", - "swoft/devtool": "^1.0", - "swoft/session": "^1.0", - "swoft/i18n": "^1.0", - "swoft/process": "^1.0", - "swoft/memory": "^1.0", - "swoft/service-governance": "^1.0", - "swoft/component": "dev-master as 1.0" - }, - "require-dev": { - "eaglewu/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^5.7" - }, - "autoload": { - "psr-4": { - "App\\": "app/" - }, - "files": [ - "app/Swoft.php" - ] + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", + "license": "Apache-2.0", + "require": { + "php": ">=7.0", + "swoft/framework": "^1.0", + "swoft/rpc": "^1.0", + "swoft/rpc-server": "^1.0", + "swoft/rpc-client": "^1.0", + "swoft/http-server": "^1.0", + "swoft/http-client": "^1.0", + "swoft/websocket-server": "^1.0", + "swoft/task": "^1.0", + "swoft/http-message": "^1.0", + "swoft/view": "^1.0", + "swoft/db": "^1.0", + "swoft/cache": "^1.0", + "swoft/redis": "^1.0", + "swoft/console": "^1.0", + "swoft/devtool": "^1.0", + "swoft/session": "^1.0", + "swoft/i18n": "^1.0", + "swoft/process": "^1.0", + "swoft/memory": "^1.0", + "swoft/service-governance": "^1.0" + }, + "require-dev": { + "eaglewu/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^5.7" + }, + "autoload": { + "psr-4": { + "App\\": "app/" }, - "autoload-dev": { - "psr-4": { - "Swoft\\Test\\": "test/" - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "test": "./vendor/bin/phpunit -c phpunit.xml" - }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/swoft-cloud/swoft-component" - }, - { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" - } + "files": [ + "app/Swoft.php" ] + }, + "autoload-dev": { + "psr-4": { + "Swoft\\Test\\": "test/" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml" + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" + } + } } From 3c8a883c7e168a77e35896cd7c65f4cf24e62fc6 Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Fri, 27 Apr 2018 23:49:15 +0800 Subject: [PATCH 227/643] add template of validator --- app/Controllers/TaskController.php | 2 +- app/Controllers/ValidatorController.php | 50 +++++++++++++++++++++++++ app/Tasks/SyncTask.php | 5 ++- test/Cases/ValidatorControllerTest.php | 17 ++++++++- 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php index e8d1c982..a0a46dfc 100644 --- a/app/Controllers/TaskController.php +++ b/app/Controllers/TaskController.php @@ -25,7 +25,7 @@ public function batch() { $count = 0; $result = []; - while ($count < 10000){ + while ($count < 50000){ $result[] = Task::deliver('sync', 'batchTask', [], Task::TYPE_ASYNC); $count++; } diff --git a/app/Controllers/ValidatorController.php b/app/Controllers/ValidatorController.php index 124ac93c..078e0d5c 100644 --- a/app/Controllers/ValidatorController.php +++ b/app/Controllers/ValidatorController.php @@ -45,6 +45,16 @@ public function string(Request $request, string $name) return [$getName, $postName, $name]; } + /** + * @RequestMapping("stringTpl") + * @Strings(from=ValidatorFrom::GET, name="name", min=3, max=10, template="{name}-{min}-{max} must") + * @return string + */ + public function stringTpl() + { + return 'stringTpl'; + } + /** * @RequestMapping("number/{id}") * @@ -65,6 +75,16 @@ public function number(Request $request, int $id) return [$get, $post, $id]; } + /** + * @RequestMapping("numberTpl") + * @Number(from=ValidatorFrom::GET, name="id", min=5, max=10, template="{name}-{min}-{max} must") + * @return string + */ + public function numberTpl() + { + return 'numberTpl'; + } + /** * @RequestMapping("integer/{id}") * @@ -85,6 +105,16 @@ public function integer(Request $request, int $id) return [$get, $post, $id]; } + /** + * @RequestMapping("integerTpl") + * @Integer(from=ValidatorFrom::GET, name="id", min=5, max=10, template="{name}-{min}-{max} must") + * @return string + */ + public function integerTpl() + { + return 'integerTpl'; + } + /** * @RequestMapping("float/{id}") * @@ -105,6 +135,16 @@ public function float(Request $request, float $id) return [$get, $post, $id]; } + /** + * @RequestMapping("floatTpl") + * @Floats(from=ValidatorFrom::GET, name="id", min=5.1, max=5.9, template="{name}-{min}-{max} must") + * @return string + */ + public function floatTpl() + { + return 'floatTpl'; + } + /** * @RequestMapping("enum/{name}") @@ -126,4 +166,14 @@ public function estring(Request $request, $name) return [$getName, $postName, $name]; } + /** + * @RequestMapping("enumTpl") + * @Enum(from=ValidatorFrom::GET, name="name", values={1,"a",3}, template="{name}-{value} must") + * @return string + */ + public function enumTpl() + { + return 'enumTpl'; + } + } \ No newline at end of file diff --git a/app/Tasks/SyncTask.php b/app/Tasks/SyncTask.php index 8e187c7a..ecaa8059 100644 --- a/app/Tasks/SyncTask.php +++ b/app/Tasks/SyncTask.php @@ -158,7 +158,10 @@ public function rpc2() public function batchTask(){ sleep(mt_rand(1, 2)); - return 'batchTask'; + + /* @var User $user*/ + $user = User::findById(80368)->getResult(); + return $user->toJson(); } /** diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php index e856fbd9..6979376b 100644 --- a/test/Cases/ValidatorControllerTest.php +++ b/test/Cases/ValidatorControllerTest.php @@ -2,7 +2,7 @@ /** * This file is part of Swoft. * - * @link https://swoft.org + * @link https://swoft.org * @document https://doc.swoft.org * @contact group@swoft.org * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE @@ -49,6 +49,9 @@ public function testString() $response = $this->request('POST', '/validator/string/swoftPath?name=swoftGet', ['name' => 'swoftPost'], parent::ACCEPT_JSON); $response->assertExactJson(['swoftGet', 'swoftPost', 'swoftPath']); + + $response = $this->request('GET', '/validator/stringTpl', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'name-3-10 must']); } /** @@ -85,6 +88,9 @@ public function testNumber() $response = $this->request('POST', '/validator/number/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); $response->assertExactJson(['9', '9', 9]); + + $response = $this->request('GET', '/validator/numberTpl', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'id-5-10 must']); } /** @@ -128,6 +134,9 @@ public function testFloat() $response = $this->request('POST', '/validator/float/5.2?id=5.2', ['id' => '5.2'], parent::ACCEPT_JSON); $response->assertExactJson(['5.2', '5.2', 5.2]); + + $response = $this->request('GET', '/validator/floatTpl', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'id-5.1-5.9 must']); } /** @@ -164,6 +173,9 @@ public function testInteger() $response = $this->request('POST', '/validator/integer/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); $response->assertExactJson(['9', '9', 9]); + + $response = $this->request('GET', '/validator/integerTpl', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'id-5-10 must']); } /** @@ -182,5 +194,8 @@ public function testEnum() $response = $this->request('POST', '/validator/enum/1?name=a', ['name' => '3'], parent::ACCEPT_JSON); $response->assertExactJson(['a', '3', '1']); + + $response = $this->request('GET', '/validator/enumTpl', [], parent::ACCEPT_JSON); + $response->assertExactJson(['message' => 'name-null must']); } } \ No newline at end of file From 7ee907d47401e7ed79a8e5c947eb5da186644ace Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Thu, 3 May 2018 10:17:13 +0800 Subject: [PATCH 228/643] add JsonSerializable --- app/Controllers/OrmController.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/Controllers/OrmController.php b/app/Controllers/OrmController.php index 281cbc92..1a4978e3 100644 --- a/app/Controllers/OrmController.php +++ b/app/Controllers/OrmController.php @@ -35,6 +35,34 @@ public function save() return [$userId]; } + public function retEntity() + { + $user = new User(); + $user->setName('name'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $userId = $user->save()->getResult(); + $user = User::findById($userId)->getResult(); + + return $user; + } + + public function retEntitys() + { + $user = new User(); + $user->setName('name'); + $user->setSex(1); + $user->setDesc('this my desc'); + $user->setAge(mt_rand(1, 100)); + + $userId = $user->save()->getResult(); + $users = User::findByIds([$userId])->getResult(); + + return $users; + } + public function findById() { $result = User::findById(41710)->getResult(); From 572dc8d39b9b25afebe13077d6ecad82a2888372 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 3 May 2018 10:20:59 +0800 Subject: [PATCH 229/643] Update .env.example --- .env.example | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.example b/.env.example index dcd08a49..6981ef25 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,7 @@ # Application TIME_ZONE=Asia/Shanghai LOG_ENABLE=false +APP_DEBUG=false # Server PFILE=/tmp/swoft.pid From 460ca6dc86e896afdea5a2ba06a9c3f73f3d39f6 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 3 May 2018 10:23:36 +0800 Subject: [PATCH 230/643] Update app.php --- config/properties/app.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/properties/app.php b/config/properties/app.php index 6e3cb07a..2c6347a9 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -8,6 +8,8 @@ */ return [ + 'env' => env('APP_ENV', 'test'), + 'debug' => env('APP_DEBUG', false), 'version' => '1.0', 'autoInitBean' => true, 'bootScan' => [ @@ -31,7 +33,6 @@ 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', ], - 'env' => 'Base', 'db' => require __DIR__ . DS . 'db.php', 'cache' => require __DIR__ . DS . 'cache.php', 'service' => require __DIR__ . DS . 'service.php', From 8f04aaf2c082e1f5c369bb2e7460d5b176c49f39 Mon Sep 17 00:00:00 2001 From: twosee Date: Fri, 4 May 2018 13:07:51 +0800 Subject: [PATCH 231/643] Correct the words, clear useless code. --- app/Controllers/DemoController.php | 1 - app/Controllers/IndexController.php | 9 ++++----- app/Exception/SwoftExceptionHandler.php | 2 +- test/Cases/IndexControllerTest.php | 2 +- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php index 6aef0fc6..5c50e468 100644 --- a/app/Controllers/DemoController.php +++ b/app/Controllers/DemoController.php @@ -18,7 +18,6 @@ use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Http\Server\Bean\Annotation\RequestMethod; use Swoft\View\Bean\Annotation\View; -use Swoft\Task\Task; use Swoft\Core\Application; use Swoft\Http\Message\Server\Request; diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php index 22702891..91f03d52 100644 --- a/app/Controllers/IndexController.php +++ b/app/Controllers/IndexController.php @@ -11,7 +11,6 @@ namespace App\Controllers; use Swoft\App; -use Swoft\Core\Coroutine; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; use Swoft\Log\Log; @@ -37,7 +36,7 @@ public function index(): array $name = 'Swoft'; $notes = [ 'New Generation of PHP Framework', - 'Hign Performance, Coroutine and Full Stack' + 'High Performance, Coroutine and Full Stack' ]; $links = [ [ @@ -73,7 +72,7 @@ public function templateView(): Response $name = 'Swoft View'; $notes = [ 'New Generation of PHP Framework', - 'Hign Performance, Coroutine and Full Stack' + 'High Performance, Coroutine and Full Stack' ]; $links = [ [ @@ -118,7 +117,7 @@ public function toArray(): array { return [ 'name' => 'Swoft', - 'notes' => ['New Generation of PHP Framework', 'Hign Performance, Coroutine and Full Stack'], + 'notes' => ['New Generation of PHP Framework', 'High Performance, Coroutine and Full Stack'], 'links' => [ [ 'name' => 'Home', @@ -155,7 +154,7 @@ public function absolutePath(): Response { $data = [ 'name' => 'Swoft', - 'notes' => ['New Generation of PHP Framework', 'Hign Performance, Coroutine and Full Stack'], + 'notes' => ['New Generation of PHP Framework', 'High Performance, Coroutine and Full Stack'], 'links' => [ [ 'name' => 'Home', diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php index 7e9e583d..242049f2 100644 --- a/app/Exception/SwoftExceptionHandler.php +++ b/app/Exception/SwoftExceptionHandler.php @@ -114,7 +114,7 @@ public function handlerViewException(Request $request, Response $response, \Thro $name = $throwable->getMessage(). $request->getUri()->getPath(); $notes = [ 'New Generation of PHP Framework', - 'Hign Performance, Coroutine and Full Stack', + 'High Performance, Coroutine and Full Stack', ]; $links = [ [ diff --git a/test/Cases/IndexControllerTest.php b/test/Cases/IndexControllerTest.php index 0fcdcaba..4aca2fd1 100644 --- a/test/Cases/IndexControllerTest.php +++ b/test/Cases/IndexControllerTest.php @@ -32,7 +32,7 @@ public function testIndex() 'name' => 'Swoft', 'notes' => [ 'New Generation of PHP Framework', - 'Hign Performance, Coroutine and Full Stack' + 'High Performance, Coroutine and Full Stack' ], 'links' => [ [ From 15f45db8bad698f917bbb519c25e8664c90b23ca Mon Sep 17 00:00:00 2001 From: lilin <794774870@qq.com> Date: Mon, 14 May 2018 23:28:27 +0800 Subject: [PATCH 232/643] modify beanscan --- config/properties/app.php | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/config/properties/app.php b/config/properties/app.php index 2c6347a9..31ee66e9 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -16,19 +16,8 @@ 'App\Commands', 'App\Boot', ], - 'beanScan' => [ - 'App\Controllers', - 'App\Models', - 'App\Middlewares', - 'App\Tasks', - 'App\Services', - 'App\Breaker', - 'App\Pool', - 'App\Exception', - 'App\Listener', - 'App\Process', - 'App\Fallback', - 'App\WebSocket', + 'excludeScan' => [ + ], 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', @@ -37,5 +26,5 @@ 'cache' => require __DIR__ . DS . 'cache.php', 'service' => require __DIR__ . DS . 'service.php', 'breaker' => require __DIR__ . DS . 'breaker.php', - 'provider' => require __DIR__ . DS . 'provider.php', + 'provider' => require __DIR__ . DS . 'provider.php', ]; From 9e9213f0a5f3fcb013dfca5d00f12ccb93791108 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Thu, 24 May 2018 13:45:42 +0800 Subject: [PATCH 233/643] use swoft/swoole-ide-helper (#250) * use swoft/swoole-ide-helper * Update composer.json and dev.composer.json Use 2.1.3 version not master version --- composer.json | 2 +- dev.composer.json | 2 +- docker-compose.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 80daea5c..ed422c9c 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "swoft/service-governance": "^1.0" }, "require-dev": { - "eaglewu/swoole-ide-helper": "dev-master", + "swoft/swoole-ide-helper": "2.1.3", "phpunit/phpunit": "^5.7" }, "autoload": { diff --git a/dev.composer.json b/dev.composer.json index 984e79d2..7b3335bc 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -33,7 +33,7 @@ "swoft/component": "dev-master as 1.0" }, "require-dev": { - "eaglewu/swoole-ide-helper": "dev-master", + "swoft/swoole-ide-helper": "2.1.3", "phpunit/phpunit": "^5.7" }, "autoload": { diff --git a/docker-compose.yml b/docker-compose.yml index bbbaea52..74731cd4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,8 @@ -version: '2' +version: '3' services: swoft: container_name: swoft - image: swoft/swoft:latest + image: swoft/swoft ports: - "80:80" volumes: From 772bc208032e011ed34fcda4d30523c0d6443cc0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 29 May 2018 11:45:45 +0800 Subject: [PATCH 234/643] swoft/swoole-ide-helper use master --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ed422c9c..b388e8b6 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,7 @@ "swoft/service-governance": "^1.0" }, "require-dev": { - "swoft/swoole-ide-helper": "2.1.3", + "swoft/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^5.7" }, "autoload": { From 545266003792544c4cc975416c3e059daa562a55 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Thu, 31 May 2018 15:46:43 +0800 Subject: [PATCH 235/643] fix #219 (#261) --- .gitignore | 1 + Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 36b10d75..610d543f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ temp/ .phpintel/ .env .DS_Store +public/devtool/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 01b3602e..2112dbdd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ RUN apt-get update \ libz-dev \ libssl-dev \ libnghttp2-dev \ + libpcre3-dev \ && apt-get clean \ && apt-get autoremove From 3fcc142208eb3124ff9d3f765ccba9651f827646 Mon Sep 17 00:00:00 2001 From: stelin Date: Sat, 2 Jun 2018 00:35:38 +0800 Subject: [PATCH 236/643] =?UTF-8?q?=E6=96=B0=E5=A2=9EQQ=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26aae8c7..68643b12 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The first high-performance PHP coroutine full-stack componentization framework b [**Chinese Document**](https://doc.swoft.org) [**English Document**](https://doc.swoft.org) Not yet, please help us write it. -QQ Group: 548173319 +QQ Group: 548173319/778656850 ## Environmental Requirements From 4f8f9fcaea6d3f7df3cbc7af8123272d327ae05e Mon Sep 17 00:00:00 2001 From: Panda <1017109588@qq.com> Date: Sun, 3 Jun 2018 06:09:08 +0800 Subject: [PATCH 237/643] Install bcmath (#263) Install bcmath fix #262 --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 2112dbdd..e60c04f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,9 @@ RUN pecl install redis && docker-php-ext-enable redis && pecl clear-cache RUN docker-php-ext-install pdo_mysql +# Install bcmath +RUN docker-php-ext-install bcmath + RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz \ && mkdir -p hiredis \ && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \ From 0d150464f7d22457760e130f38c75831c86e383d Mon Sep 17 00:00:00 2001 From: Panda <1017109588@qq.com> Date: Sun, 3 Jun 2018 09:30:32 +0800 Subject: [PATCH 238/643] composer.json add ext-swoole (#264) composer.json add ext-swoole --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index b388e8b6..867e0787 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "license": "Apache-2.0", "require": { "php": ">=7.0", + "ext-swoole": ">=2.1", "swoft/framework": "^1.0", "swoft/rpc": "^1.0", "swoft/rpc-server": "^1.0", From 58b863e05c271887320964f7f2e9251327e8a3b4 Mon Sep 17 00:00:00 2001 From: Daniel Ruf Date: Wed, 6 Jun 2018 22:27:04 +0200 Subject: [PATCH 239/643] chore: cache dependencies (#270) * chore: cache dependencies * chore: trigger new build --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index e54460c7..ad955eb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,3 +12,7 @@ before_script: - composer update --dev script: composer test + +cache: + directories: + - "$HOME/.composer/cache/files" \ No newline at end of file From f4e69c426da0e1962a62e24992cf9114afd982ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sat, 9 Jun 2018 22:32:18 +0800 Subject: [PATCH 240/643] Upgrade swoole to v2.2.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e60c04f3..59183381 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,7 +41,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. ) \ && rm -r hiredis -RUN wget https://github.com/swoole/swoole-src/archive/v2.1.3.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v2.2.0.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From 5c97791dec074351f1c1be829d0dbc6f70464799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sat, 9 Jun 2018 22:34:08 +0800 Subject: [PATCH 241/643] Optimize grammar --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 68643b12..2194d9a4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ## Introduction -The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. +The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. - Base on Swoole extension - Built-in HTTP, TCP, WebSocket Server From 9dfc6ab7fc4f07b937e7a81698a5553064cac75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sat, 9 Jun 2018 22:36:17 +0800 Subject: [PATCH 242/643] add php-cs-fixer (#273) --- composer.json | 122 +++++++++++++++++++++++----------------------- dev.composer.json | 24 ++++----- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/composer.json b/composer.json index 867e0787..d3836497 100644 --- a/composer.json +++ b/composer.json @@ -1,64 +1,66 @@ { - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", - "license": "Apache-2.0", - "require": { - "php": ">=7.0", - "ext-swoole": ">=2.1", - "swoft/framework": "^1.0", - "swoft/rpc": "^1.0", - "swoft/rpc-server": "^1.0", - "swoft/rpc-client": "^1.0", - "swoft/http-server": "^1.0", - "swoft/http-client": "^1.0", - "swoft/websocket-server": "^1.0", - "swoft/task": "^1.0", - "swoft/http-message": "^1.0", - "swoft/view": "^1.0", - "swoft/db": "^1.0", - "swoft/cache": "^1.0", - "swoft/redis": "^1.0", - "swoft/console": "^1.0", - "swoft/devtool": "^1.0", - "swoft/session": "^1.0", - "swoft/i18n": "^1.0", - "swoft/process": "^1.0", - "swoft/memory": "^1.0", - "swoft/service-governance": "^1.0" - }, - "require-dev": { - "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^5.7" - }, - "autoload": { - "psr-4": { - "App\\": "app/" - }, - "files": [ - "app/Swoft.php" - ] - }, - "autoload-dev": { - "psr-4": { - "Swoft\\Test\\": "test/" - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" ], - "test": "./vendor/bin/phpunit -c phpunit.xml" - }, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", + "license": "Apache-2.0", + "require": { + "php": ">=7.0", + "ext-swoole": ">=2.1", + "swoft/framework": "^1.0", + "swoft/rpc": "^1.0", + "swoft/rpc-server": "^1.0", + "swoft/rpc-client": "^1.0", + "swoft/http-server": "^1.0", + "swoft/http-client": "^1.0", + "swoft/websocket-server": "^1.0", + "swoft/task": "^1.0", + "swoft/http-message": "^1.0", + "swoft/view": "^1.0", + "swoft/db": "^1.0", + "swoft/cache": "^1.0", + "swoft/redis": "^1.0", + "swoft/console": "^1.0", + "swoft/devtool": "^1.0", + "swoft/session": "^1.0", + "swoft/i18n": "^1.0", + "swoft/process": "^1.0", + "swoft/memory": "^1.0", + "swoft/service-governance": "^1.0" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^5.7", + "friendsofphp/php-cs-fixer": "^2.10" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Swoft.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Swoft\\Test\\": "test/" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml", + "cs-fix": "./vendor/bin/php-cs-fixer fix $1" + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" + } } - } } diff --git a/dev.composer.json b/dev.composer.json index 7b3335bc..30519b36 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -34,7 +34,8 @@ }, "require-dev": { "swoft/swoole-ide-helper": "2.1.3", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^5.7", + "friendsofphp/php-cs-fixer": "^2.10" }, "autoload": { "psr-4": { @@ -53,16 +54,17 @@ "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], - "test": "./vendor/bin/phpunit -c phpunit.xml" + "test": "./vendor/bin/phpunit -c phpunit.xml", + "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/swoft-cloud/swoft-component" - }, - { - "type": "composer", - "url": "https://packagist.phpcomposer.com/" - } + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/swoft-cloud/swoft-component" + }, + { + "type": "composer", + "url": "https://packagist.phpcomposer.com/" + } ] } From 813915db905d3d2cc1369d9f7de9a7c3df1d3299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sat, 9 Jun 2018 22:40:01 +0800 Subject: [PATCH 243/643] Update Dockerfile --- Dockerfile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 59183381..9218dc39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,9 +2,11 @@ FROM php:7.1 MAINTAINER huangzhhui +# Timezone RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone +# Libs RUN apt-get update \ && apt-get install -y \ curl \ @@ -22,13 +24,16 @@ RUN curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ && composer self-update --clean-backups +# Redis extension RUN pecl install redis && docker-php-ext-enable redis && pecl clear-cache +# PDO extension RUN docker-php-ext-install pdo_mysql -# Install bcmath +# Bcmath extension RUN docker-php-ext-install bcmath +# Hiredis RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz \ && mkdir -p hiredis \ && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \ @@ -40,7 +45,8 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && ldconfig \ ) \ && rm -r hiredis - + +# Swoole extension RUN wget https://github.com/swoole/swoole-src/archive/v2.2.0.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ @@ -58,6 +64,7 @@ RUN wget https://github.com/swoole/swoole-src/archive/v2.2.0.tar.gz -O swoole.ta ADD . /var/www/swoft WORKDIR /var/www/swoft + RUN composer install --no-dev \ && composer dump-autoload -o \ && composer clearcache From cd6207709560ee16bebc89e8a4d19522d5ad0daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sat, 9 Jun 2018 23:07:57 +0800 Subject: [PATCH 244/643] add multiFilesInOneKey action to Psr7Controller (#275) --- app/Controllers/Psr7Controller.php | 58 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php index aba85a4a..bb420fcd 100644 --- a/app/Controllers/Psr7Controller.php +++ b/app/Controllers/Psr7Controller.php @@ -11,17 +11,12 @@ namespace App\Controllers; use Psr\Http\Message\UploadedFileInterface; +use Swoft\Http\Message\Server\Request; use Swoft\Http\Server\Bean\Annotation\Controller; use Swoft\Http\Server\Bean\Annotation\RequestMapping; -use Swoft\Http\Message\Server\Request; /** * @Controller(prefix="/psr7") - * @uses Psr7Controller - * @version 2017-11-05 - * @author huangzhhui - * @copyright Copyright 2010-2017 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} */ class Psr7Controller { @@ -29,10 +24,9 @@ class Psr7Controller /** * @RequestMapping() * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function get(Request $request) + public function get(Request $request): array { $param1 = $request->query('param1'); $param2 = $request->query('param2', 'defaultValue'); @@ -41,12 +35,10 @@ public function get(Request $request) /** * @RequestMapping() - * * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function post(Request $request) + public function post(Request $request): array { $param1 = $request->post('param1'); $param2 = $request->post('param2'); @@ -55,12 +47,10 @@ public function post(Request $request) /** * @RequestMapping() - * * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function input(Request $request) + public function input(Request $request): array { $param1 = $request->input('param1'); $inputs = $request->input(); @@ -74,10 +64,9 @@ public function input(Request $request) /** * @RequestMapping() * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function raw(Request $request) + public function raw(Request $request): array { $param1 = $request->raw(); return compact('param1'); @@ -86,10 +75,9 @@ public function raw(Request $request) /** * @RequestMapping() * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function cookies(Request $request) + public function cookies(Request $request): array { $cookie1 = $request->cookie(); return compact('cookie1'); @@ -97,12 +85,10 @@ public function cookies(Request $request) /** * @RequestMapping() - * * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function header(Request $request) + public function header(Request $request): array { $header1 = $request->header(); $host = $request->header('host'); @@ -112,10 +98,9 @@ public function header(Request $request) /** * @RequestMapping() * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function json(Request $request) + public function json(Request $request): array { $json = $request->json(); $jsonParam = $request->json('jsonParam'); @@ -125,16 +110,15 @@ public function json(Request $request) /** * @RequestMapping() * @param \Swoft\Http\Message\Server\Request $request - * * @return array */ - public function files(Request $request) + public function files(Request $request): array { $files = $request->file(); foreach ($files as $file) { if ($file instanceof UploadedFileInterface) { try { - $file->moveTo('@runtime/uploadfiles/1.png'); + $file->moveTo('@runtime/uploadfiles/' . $file->getClientFilename()); $move = true; } catch (\Throwable $e) { $move = false; @@ -145,4 +129,26 @@ public function files(Request $request) return compact('move'); } + /** + * @RequestMapping() + * @param \Swoft\Http\Message\Server\Request $request + * @return array|null|\Swoft\Http\Message\Upload\UploadedFile + */ + public function multiFilesInOneKey(Request $request) + { + $files = $request->file('files'); + foreach ($files as $file) { + if ($file instanceof UploadedFileInterface) { + try { + $file->moveTo('@runtime/uploadfiles/' . $file->getClientFilename()); + $move[$file->getClientFilename()] = true; + } catch (\Throwable $e) { + $move[$file->getClientFilename()] = false; + } + } + } + + return compact('move'); + } + } \ No newline at end of file From 5c74a041530b9dd04298ffbd8072b8a820fd2865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Thu, 14 Jun 2018 18:07:38 +0800 Subject: [PATCH 245/643] Update Dockerfile Upgrade swoole to v4.0.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9218dc39..1ae4c2af 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,7 +47,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && rm -r hiredis # Swoole extension -RUN wget https://github.com/swoole/swoole-src/archive/v2.2.0.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v4.0.0.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ From 9369bc5591bf9d9cdf96610ebe6228a72c4d82be Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 19 Jun 2018 09:17:51 +0800 Subject: [PATCH 246/643] add TCP_PACKAGE_EOF setting for tcp (#286) --- config/server.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config/server.php b/config/server.php index c0dd2b19..12507aa5 100644 --- a/config/server.php +++ b/config/server.php @@ -18,15 +18,16 @@ 'tcp' => [ 'host' => env('TCP_HOST', '0.0.0.0'), 'port' => env('TCP_PORT', 8099), - 'mode' => env('TCP_MODE', SWOOLE_PROCESS), + 'mode' => env('TCP_MODE', SWOOLE_PROCESS), 'type' => env('TCP_TYPE', SWOOLE_SOCK_TCP), 'package_max_length' => env('TCP_PACKAGE_MAX_LENGTH', 2048), 'open_eof_check' => env('TCP_OPEN_EOF_CHECK', false), + 'package_eof' => "\r\n", ], 'http' => [ 'host' => env('HTTP_HOST', '0.0.0.0'), 'port' => env('HTTP_PORT', 80), - 'mode' => env('HTTP_MODE', SWOOLE_PROCESS), + 'mode' => env('HTTP_MODE', SWOOLE_PROCESS), 'type' => env('HTTP_TYPE', SWOOLE_SOCK_TCP), ], 'ws' => [ From 850c818ef97388acbcb44665c1a1bd8474ee3eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 29 Jun 2018 14:14:26 +0800 Subject: [PATCH 247/643] Update Dockerfile Upgrade swoole to 4.0.1 version and remove configure argument --enable-coroutine --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1ae4c2af..d2900ae4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,14 +47,14 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && rm -r hiredis # Swoole extension -RUN wget https://github.com/swoole/swoole-src/archive/v4.0.0.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v4.0.1.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ && ( \ cd swoole \ && phpize \ - && ./configure --enable-async-redis --enable-mysqlnd --enable-coroutine --enable-openssl --enable-http2 \ + && ./configure --enable-async-redis --enable-mysqlnd --enable-openssl --enable-http2 \ && make -j$(nproc) \ && make install \ ) \ From 909d37cc4d527bf63b63530abf899356a12d063f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 29 Jun 2018 16:17:26 +0800 Subject: [PATCH 248/643] Update composer.json Add PsySH --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index d3836497..06444292 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "require-dev": { "swoft/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^5.7", - "friendsofphp/php-cs-fixer": "^2.10" + "friendsofphp/php-cs-fixer": "^2.10", + "psy/psysh": "@stable" }, "autoload": { "psr-4": { From 4fd66cc632683da15e8890678bd0b7f696a33cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 29 Jun 2018 16:17:47 +0800 Subject: [PATCH 249/643] Update dev.composer.json add PsySH --- dev.composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev.composer.json b/dev.composer.json index 30519b36..49cf298f 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -35,7 +35,8 @@ "require-dev": { "swoft/swoole-ide-helper": "2.1.3", "phpunit/phpunit": "^5.7", - "friendsofphp/php-cs-fixer": "^2.10" + "friendsofphp/php-cs-fixer": "^2.10", + "psy/psysh": "@stable" }, "autoload": { "psr-4": { From f6c937f296342d1f76179734220bd8ca8d896c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 29 Jun 2018 16:18:30 +0800 Subject: [PATCH 250/643] Update dev.composer.json use ssh instead of https --- dev.composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev.composer.json b/dev.composer.json index 49cf298f..51ef7ec4 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -60,8 +60,8 @@ }, "repositories": [ { - "type": "vcs", - "url": "https://github.com/swoft-cloud/swoft-component" + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-component.git" }, { "type": "composer", From 90a6dd537614ae59f7f012e1548a3a0b410e108d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Wed, 4 Jul 2018 12:36:32 +0800 Subject: [PATCH 251/643] Add phpstorm meta file and fix validator unit test (#300) * fix ValidatorController unit test * add .phpstorm.meta.php for IDE * add open_eof_split --- .phpstorm.meta.php | 20 +++++++ config/server.php | 1 + test/Cases/ValidatorControllerTest.php | 82 +++++++++++++------------- 3 files changed, 62 insertions(+), 41 deletions(-) create mode 100644 .phpstorm.meta.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php new file mode 100644 index 00000000..fff23cc3 --- /dev/null +++ b/.phpstorm.meta.php @@ -0,0 +1,20 @@ + \Swoft\Defer\Defer::class, + ]; + override(\bean(0), map($map)); + override(\Swoft\App::getBean(0), map($map)); + override(\Swoft\Core\ApplicationContext::getBean(0), map($map)); + override(\Swoft\Bean\BeanFactory::getBean(0), map($map)); + +} \ No newline at end of file diff --git a/config/server.php b/config/server.php index 12507aa5..0f25c2bc 100644 --- a/config/server.php +++ b/config/server.php @@ -22,6 +22,7 @@ 'type' => env('TCP_TYPE', SWOOLE_SOCK_TCP), 'package_max_length' => env('TCP_PACKAGE_MAX_LENGTH', 2048), 'open_eof_check' => env('TCP_OPEN_EOF_CHECK', false), + 'open_eof_split' => env('TCP_OPEN_EOF_SPLIT', true), 'package_eof' => "\r\n", ], 'http' => [ diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php index 6979376b..53f79c97 100644 --- a/test/Cases/ValidatorControllerTest.php +++ b/test/Cases/ValidatorControllerTest.php @@ -30,28 +30,28 @@ public function testString() $response->assertExactJson(['boy', 'girl', 'swoft']); $response = $this->request('POST', '/validator/string/c', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name length is too short (minimum is 3)']); + $response->assertJsonFragment(['message' => 'Parameter name length is too short (minimum is 3)']); $response = $this->request('POST', '/validator/string/swoft', ['name' => 'a'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name length is too short (minimum is 3)']); + $response->assertJsonFragment(['message' => 'Parameter name length is too short (minimum is 3)']); $response = $this->request('POST', '/validator/string/swoft?name=b', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name length is too short (minimum is 3)']); + $response->assertJsonFragment(['message' => 'Parameter name length is too short (minimum is 3)']); $response = $this->request('POST', '/validator/string/swoft66666666', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name length is too long (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter name length is too long (maximum is 10)']); $response = $this->request('POST', '/validator/string/swoft', ['name' => 'swoft66666666'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name length is too long (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter name length is too long (maximum is 10)']); $response = $this->request('POST', '/validator/string/swoft?name=swoft66666666', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name length is too long (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter name length is too long (maximum is 10)']); $response = $this->request('POST', '/validator/string/swoftPath?name=swoftGet', ['name' => 'swoftPost'], parent::ACCEPT_JSON); $response->assertExactJson(['swoftGet', 'swoftPost', 'swoftPath']); $response = $this->request('GET', '/validator/stringTpl', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'name-3-10 must']); + $response->assertJsonFragment(['message' => 'name-3-10 must']); } /** @@ -63,34 +63,34 @@ public function testNumber() $response->assertExactJson([7, 8, 10]); $response = $this->request('POST', '/validator/number/3', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/6', ['id' => '-2'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not a number']); + $response->assertJsonFragment(['message' => 'Parameter id is not a number']); $response = $this->request('POST', '/validator/number/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/6?id=-2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not a number']); + $response->assertJsonFragment(['message' => 'Parameter id is not a number']); $response = $this->request('POST', '/validator/number/6?id=2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/number/12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9?id=12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/number/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); $response->assertExactJson(['9', '9', 9]); $response = $this->request('GET', '/validator/numberTpl', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'id-5-10 must']); + $response->assertJsonFragment(['message' => 'id-5-10 must']); } /** @@ -99,44 +99,44 @@ public function testNumber() public function testFloat() { $response = $this->request('GET', '/validator/float/a', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not float type']); + $response->assertJsonFragment(['message' => 'Parameter id is not float type']); $response = $this->request('GET', '/validator/float/5', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not float type']); + $response->assertJsonFragment(['message' => 'Parameter id is not float type']); $response = $this->request('POST', '/validator/float/5.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/float/6.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 5)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => 5], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not float type']); + $response->assertJsonFragment(['message' => 'Parameter id is not float type']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.0'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '6.0'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 5)']); $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.2'], parent::ACCEPT_JSON); $response->assertExactJson([5.6, '5.2', 5.2]); $response = $this->request('POST', '/validator/float/5.2?id=5', [5.2], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not float type']); + $response->assertJsonFragment(['message' => 'Parameter id is not float type']); $response = $this->request('POST', '/validator/float/5.2?id=5.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/float/5.2?id=6.0', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 5)']); $response = $this->request('POST', '/validator/float/5.2?id=5.2', ['id' => '5.2'], parent::ACCEPT_JSON); $response->assertExactJson(['5.2', '5.2', 5.2]); $response = $this->request('GET', '/validator/floatTpl', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'id-5.1-5.9 must']); + $response->assertJsonFragment(['message' => 'id-5.1-5.9 must']); } /** @@ -148,54 +148,54 @@ public function testInteger() $response->assertExactJson([7, 8, 10]); $response = $this->request('POST', '/validator/integer/3', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/6', ['id' => 'a'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not integer type']); + $response->assertJsonFragment(['message' => 'Parameter id is not integer type']); $response = $this->request('POST', '/validator/integer/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/6?id=a', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is not integer type']); + $response->assertJsonFragment(['message' => 'Parameter id is not integer type']); $response = $this->request('POST', '/validator/integer/6?id=2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too small (minimum is 5)']); + $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); $response = $this->request('POST', '/validator/integer/12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9?id=12', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter id is too big (maximum is 10)']); + $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); $response = $this->request('POST', '/validator/integer/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); $response->assertExactJson(['9', '9', 9]); $response = $this->request('GET', '/validator/integerTpl', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'id-5-10 must']); + $response->assertJsonFragment(['message' => 'id-5-10 must']); } /** - * @covers \App\Controllers\ValidatorController::enum + * @covers \App\Controllers\ValidatorController::estring */ public function testEnum() { $response = $this->request('POST', '/validator/enum/4', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name is an invalid enum value']); + $response->assertJsonFragment(['message' => 'Parameter name is an invalid enum value']); $response = $this->request('POST', '/validator/enum/1?name=4', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name is an invalid enum value']); + $response->assertJsonFragment(['message' => 'Parameter name is an invalid enum value']); $response = $this->request('POST', '/validator/enum/1', ['name' => '4'], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'Parameter name is an invalid enum value']); + $response->assertJsonFragment(['message' => 'Parameter name is an invalid enum value']); $response = $this->request('POST', '/validator/enum/1?name=a', ['name' => '3'], parent::ACCEPT_JSON); $response->assertExactJson(['a', '3', '1']); $response = $this->request('GET', '/validator/enumTpl', [], parent::ACCEPT_JSON); - $response->assertExactJson(['message' => 'name-null must']); + $response->assertJsonFragment(['message' => 'name-null must']); } } \ No newline at end of file From e034f35f04013bb5f561181301b4ab7c94295bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 6 Jul 2018 13:16:35 +0800 Subject: [PATCH 252/643] Update composer.json Upgrade db to v1.1 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 06444292..5c17ff85 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "swoft/task": "^1.0", "swoft/http-message": "^1.0", "swoft/view": "^1.0", - "swoft/db": "^1.0", + "swoft/db": "^1.1", "swoft/cache": "^1.0", "swoft/redis": "^1.0", "swoft/console": "^1.0", From d6ffcb474581c002f96f19e83a94fef1d21a51cd Mon Sep 17 00:00:00 2001 From: stelin Date: Tue, 24 Jul 2018 10:59:55 +0800 Subject: [PATCH 253/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2194d9a4..0bd4dc34 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The first high-performance PHP coroutine full-stack componentization framework b [**Chinese Document**](https://doc.swoft.org) [**English Document**](https://doc.swoft.org) Not yet, please help us write it. -QQ Group: 548173319/778656850 +QQ Group1: 548173319(full) QQ Group2: 778656850 ## Environmental Requirements From 1bebfdcef3a2ef5e7ce23d04559f4e3c71a3c09e Mon Sep 17 00:00:00 2001 From: stelin Date: Tue, 24 Jul 2018 11:00:50 +0800 Subject: [PATCH 254/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bd4dc34..666d42b5 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The first high-performance PHP coroutine full-stack componentization framework b [**Chinese Document**](https://doc.swoft.org) [**English Document**](https://doc.swoft.org) Not yet, please help us write it. -QQ Group1: 548173319(full) QQ Group2: 778656850 +QQ Group1: 548173319(full) QQ Group2: 778656850 ## Environmental Requirements From 1914b411112e64610bd3b37ca48d3aa0692fce38 Mon Sep 17 00:00:00 2001 From: stelin Date: Tue, 24 Jul 2018 11:01:57 +0800 Subject: [PATCH 255/643] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 666d42b5..712da9a0 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,8 @@ The first high-performance PHP coroutine full-stack componentization framework b [**Chinese Document**](https://doc.swoft.org) [**English Document**](https://doc.swoft.org) Not yet, please help us write it. -QQ Group1: 548173319(full) QQ Group2: 778656850 +QQ Group1: 548173319(full) +QQ Group2: 778656850 ## Environmental Requirements From ae5af7fff9c6a5dcc8f38d97b42aba8f0f04d69d Mon Sep 17 00:00:00 2001 From: stelin Date: Tue, 24 Jul 2018 11:03:44 +0800 Subject: [PATCH 256/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 712da9a0..87cfd78d 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ The first high-performance PHP coroutine full-stack componentization framework b [**Chinese Document**](https://doc.swoft.org) [**English Document**](https://doc.swoft.org) Not yet, please help us write it. -QQ Group1: 548173319(full) +QQ Group1: 548173319(Full) QQ Group2: 778656850 ## Environmental Requirements From 61b73354692b693b958a26537bf08b9b91088564 Mon Sep 17 00:00:00 2001 From: stelin Date: Tue, 24 Jul 2018 13:22:52 +0800 Subject: [PATCH 257/643] Update README_CN.md --- README_CN.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 99bf2d1c..01724604 100644 --- a/README_CN.md +++ b/README_CN.md @@ -48,7 +48,8 @@ [**中文文档**](https://doc.swoft.org) -QQ 交流群: 548173319 +QQ 交流群1: 548173319(已满) +QQ 交流群2: 778656850 ## 环境要求 From 80c58612b38d5b3b0e35d95a0de823504c97c339 Mon Sep 17 00:00:00 2001 From: whiteCcinn <471113744@qq.com> Date: Thu, 26 Jul 2018 18:22:55 +0800 Subject: [PATCH 258/643] add docker-compose env contents --- .env.example.docker-compose | 114 ++++++++++++++++++++++++++++++++++++ Dockerfile | 19 ++++-- demo.sql | 16 +++++ docker-compose.yml | 33 ++++++++++- 4 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 .env.example.docker-compose create mode 100644 demo.sql diff --git a/.env.example.docker-compose b/.env.example.docker-compose new file mode 100644 index 00000000..018e67df --- /dev/null +++ b/.env.example.docker-compose @@ -0,0 +1,114 @@ +# Application +TIME_ZONE=Asia/Shanghai +LOG_ENABLE=false +APP_DEBUG=false + +# Server +PFILE=/tmp/swoft.pid +PNAME=php-swoft +TCPABLE=true +CRONABLE=false +AUTO_RELOAD=true +AUTO_REGISTER=false + +# HTTP +HTTP_HOST=0.0.0.0 +HTTP_PORT=80 +HTTP_MODE=SWOOLE_PROCESS +HTTP_TYPE=SWOOLE_SOCK_TCP + +# WebSocket +WS_ENABLE_HTTP=true + +# TCP +TCP_HOST=0.0.0.0 +TCP_PORT=8099 +TCP_MODE=SWOOLE_PROCESS +TCP_TYPE=SWOOLE_SOCK_TCP +TCP_PACKAGE_MAX_LENGTH=2048 +TCP_OPEN_EOF_CHECK=false + +# Crontab +CRONTAB_TASK_COUNT=1024 +CRONTAB_TASK_QUEUE=2048 + +# Swoole Settings +WORKER_NUM=1 +MAX_REQUEST=100000 +DAEMONIZE=0 +DISPATCH_MODE=2 +TASK_IPC_MODE=1 +MESSAGE_QUEUE_KEY=1879052289 +TASK_TMPDIR=/tmp/ +LOG_FILE=@runtime/logs/swoole.log +TASK_WORKER_NUM=1 +PACKAGE_MAX_LENGTH=2048 +OPEN_HTTP2_PROTOCOL=false +SSL_CERT_FILE=/path/to/ssl_cert_file +SSL_KEY_FILE=/path/to/ssl_key_file + +# Database Master nodes +DB_NAME=dbMaster +DB_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 +DB_MIN_ACTIVE=5 +DB_MAX_ACTIVE=10 +DB_MAX_WAIT=20 +DB_MAX_WAIT_TIME=3 +DB_MAX_IDLE_TIME=60 +DB_TIMEOUT=2 + +# Database Slave nodes +DB_SLAVE_NAME=dbSlave +DB_SLAVE_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 +DB_SLAVE_MIN_ACTIVE=5 +DB_SLAVE_MAX_ACTIVE=10 +DB_SLAVE_MAX_WAIT=20 +DB_SLAVE_MAX_WAIT_TIME=3 +DB_SLAVE_MAX_IDLE_TIME=60 +DB_SLAVE_TIMEOUT=3 + +# Redis +REDIS_NAME=redis +REDIS_DB=2 +REDIS_URI=redis:6379,redis:6379 +REDIS_MIN_ACTIVE=5 +REDIS_MAX_ACTIVE=10 +REDIS_MAX_WAIT=20 +REDIS_MAX_WAIT_TIME=3 +REDIS_MAX_IDLE_TIME=60 +REDIS_TIMEOUT=3 +REDIS_SERIALIZE=1 + +# other redis node +REDIS_DEMO_REDIS_DB=6 +REDIS_DEMO_REDIS_PREFIX=demo_redis_ + +# User service (demo service) +USER_POOL_NAME=user +USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099 +USER_POOL_MIN_ACTIVE=5 +USER_POOL_MAX_ACTIVE=10 +USER_POOL_MAX_WAIT=20 +USER_POOL_TIMEOUT=200 +USER_POOL_MAX_WAIT_TIME=3 +USER_POOL_MAX_IDLE_TIME=60 +USER_POOL_USE_PROVIDER=false +USER_POOL_BALANCER=random +USER_POOL_PROVIDER=consul + +# User service breaker (demo service) +USER_BREAKER_FAIL_COUNT = 3 +USER_BREAKER_SUCCESS_COUNT = 6 +USER_BREAKER_DELAY_TIME = 5000 + +# Consul +CONSUL_ADDRESS=http://127.0.0.1 +CONSUL_PORT=8500 +CONSUL_REGISTER_NAME=user +CONSUL_REGISTER_ETO=false +CONSUL_REGISTER_SERVICE_ADDRESS=127.0.0.1 +CONSUL_REGISTER_SERVICE_PORT=8099 +CONSUL_REGISTER_CHECK_NAME=user +CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099 +CONSUL_REGISTER_CHECK_INTERVAL=10 +CONSUL_REGISTER_CHECK_TIMEOUT=1 diff --git a/Dockerfile b/Dockerfile index d2900ae4..4d4d8e32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,11 @@ FROM php:7.1 MAINTAINER huangzhhui +# Version +ENV PHPREDIS_VERSION 4.0.0 +ENV HIREDIS_VERSION 0.13.3 +ENV SWOOLE_VERSION 4.0.1 + # Timezone RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone @@ -20,21 +25,25 @@ RUN apt-get update \ && apt-get clean \ && apt-get autoremove +# Composer RUN curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ && composer self-update --clean-backups -# Redis extension -RUN pecl install redis && docker-php-ext-enable redis && pecl clear-cache - # PDO extension RUN docker-php-ext-install pdo_mysql # Bcmath extension RUN docker-php-ext-install bcmath +# Redis extension +RUN wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \ + && pecl install /tmp/redis.tar.tgz \ + && rm -rf /tmp/redis.tar.tgz \ + && docker-php-ext-enable redis + # Hiredis -RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz \ +RUN wget https://github.com/redis/hiredis/archive/v${HIREDIS_VERSION}.tar.gz -O hiredis.tar.gz \ && mkdir -p hiredis \ && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \ && rm hiredis.tar.gz \ @@ -47,7 +56,7 @@ RUN wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar. && rm -r hiredis # Swoole extension -RUN wget https://github.com/swoole/swoole-src/archive/v4.0.1.tar.gz -O swoole.tar.gz \ +RUN wget https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ && rm swoole.tar.gz \ diff --git a/demo.sql b/demo.sql new file mode 100644 index 00000000..9f0c0def --- /dev/null +++ b/demo.sql @@ -0,0 +1,16 @@ +CREATE DATABASE IF NOT EXISTS test; +USE test; +CREATE TABLE IF NOT EXISTS `user` ( + `id` INT (11) NOT NULL AUTO_INCREMENT, + `name` VARCHAR (20) DEFAULT NULL, + `sex` INT (1) NOT NULL DEFAULT '0', + `age` INT (1) NOT NULL DEFAULT '0', + `description` VARCHAR (240) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE = INNODB DEFAULT CHARSET = utf8 ; +CREATE TABLE `count` ( + `uid` INT (11) NOT NULL, + `fans` INT (1) NOT NULL DEFAULT '0', + `follows` INT (1) NOT NULL DEFAULT '0', + PRIMARY KEY (`uid`) +) ENGINE = INNODB DEFAULT CHARSET = utf8 ; diff --git a/docker-compose.yml b/docker-compose.yml index 74731cd4..3e27f27d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,41 @@ version: '3' +networks: + swoft-network: + driver: bridge services: + redis: + container_name: docker-compose-swoft-redis + image: redis:latest + privileged: true + ports: + - "6379:6379" + networks: + - swoft-network + + mysql: + container_name: docker-compose-swoft-mysql + image: mysql:5.6 + privileged: true + ports: + - "3306:3306" + volumes: + - ./demo.sql:/docker-entrypoint-initdb.d/demo.sql + environment: + MYSQL_ROOT_PASSWORD: 123456 + networks: + - swoft-network + swoft: - container_name: swoft - image: swoft/swoft + container_name: docker-compose-swoft + image: swoft/swoft:latest +# build: ./ ports: - "80:80" volumes: - ./:/var/www/swoft stdin_open: true tty: true + privileged: true command: php /var/www/swoft/bin/swoft start + networks: + - swoft-network \ No newline at end of file From a851e6f52cec27b08fbaa4915b8b692ef575e24b Mon Sep 17 00:00:00 2001 From: ccinn <471113744@qq.com> Date: Thu, 26 Jul 2018 20:02:12 +0800 Subject: [PATCH 259/643] Adjust docker-compose env_file (#325) --- .env.docker-compose | 8 +++ .env.example.docker-compose | 114 ------------------------------------ docker-compose.yml | 1 + 3 files changed, 9 insertions(+), 114 deletions(-) create mode 100644 .env.docker-compose delete mode 100644 .env.example.docker-compose diff --git a/.env.docker-compose b/.env.docker-compose new file mode 100644 index 00000000..ce342032 --- /dev/null +++ b/.env.docker-compose @@ -0,0 +1,8 @@ +# Database Master nodes +DB_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 + +# Database Slave nodes +DB_SLAVE_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 + +# Redis +REDIS_URI=redis:6379,redis:6379 \ No newline at end of file diff --git a/.env.example.docker-compose b/.env.example.docker-compose deleted file mode 100644 index 018e67df..00000000 --- a/.env.example.docker-compose +++ /dev/null @@ -1,114 +0,0 @@ -# Application -TIME_ZONE=Asia/Shanghai -LOG_ENABLE=false -APP_DEBUG=false - -# Server -PFILE=/tmp/swoft.pid -PNAME=php-swoft -TCPABLE=true -CRONABLE=false -AUTO_RELOAD=true -AUTO_REGISTER=false - -# HTTP -HTTP_HOST=0.0.0.0 -HTTP_PORT=80 -HTTP_MODE=SWOOLE_PROCESS -HTTP_TYPE=SWOOLE_SOCK_TCP - -# WebSocket -WS_ENABLE_HTTP=true - -# TCP -TCP_HOST=0.0.0.0 -TCP_PORT=8099 -TCP_MODE=SWOOLE_PROCESS -TCP_TYPE=SWOOLE_SOCK_TCP -TCP_PACKAGE_MAX_LENGTH=2048 -TCP_OPEN_EOF_CHECK=false - -# Crontab -CRONTAB_TASK_COUNT=1024 -CRONTAB_TASK_QUEUE=2048 - -# Swoole Settings -WORKER_NUM=1 -MAX_REQUEST=100000 -DAEMONIZE=0 -DISPATCH_MODE=2 -TASK_IPC_MODE=1 -MESSAGE_QUEUE_KEY=1879052289 -TASK_TMPDIR=/tmp/ -LOG_FILE=@runtime/logs/swoole.log -TASK_WORKER_NUM=1 -PACKAGE_MAX_LENGTH=2048 -OPEN_HTTP2_PROTOCOL=false -SSL_CERT_FILE=/path/to/ssl_cert_file -SSL_KEY_FILE=/path/to/ssl_key_file - -# Database Master nodes -DB_NAME=dbMaster -DB_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 -DB_MIN_ACTIVE=5 -DB_MAX_ACTIVE=10 -DB_MAX_WAIT=20 -DB_MAX_WAIT_TIME=3 -DB_MAX_IDLE_TIME=60 -DB_TIMEOUT=2 - -# Database Slave nodes -DB_SLAVE_NAME=dbSlave -DB_SLAVE_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 -DB_SLAVE_MIN_ACTIVE=5 -DB_SLAVE_MAX_ACTIVE=10 -DB_SLAVE_MAX_WAIT=20 -DB_SLAVE_MAX_WAIT_TIME=3 -DB_SLAVE_MAX_IDLE_TIME=60 -DB_SLAVE_TIMEOUT=3 - -# Redis -REDIS_NAME=redis -REDIS_DB=2 -REDIS_URI=redis:6379,redis:6379 -REDIS_MIN_ACTIVE=5 -REDIS_MAX_ACTIVE=10 -REDIS_MAX_WAIT=20 -REDIS_MAX_WAIT_TIME=3 -REDIS_MAX_IDLE_TIME=60 -REDIS_TIMEOUT=3 -REDIS_SERIALIZE=1 - -# other redis node -REDIS_DEMO_REDIS_DB=6 -REDIS_DEMO_REDIS_PREFIX=demo_redis_ - -# User service (demo service) -USER_POOL_NAME=user -USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099 -USER_POOL_MIN_ACTIVE=5 -USER_POOL_MAX_ACTIVE=10 -USER_POOL_MAX_WAIT=20 -USER_POOL_TIMEOUT=200 -USER_POOL_MAX_WAIT_TIME=3 -USER_POOL_MAX_IDLE_TIME=60 -USER_POOL_USE_PROVIDER=false -USER_POOL_BALANCER=random -USER_POOL_PROVIDER=consul - -# User service breaker (demo service) -USER_BREAKER_FAIL_COUNT = 3 -USER_BREAKER_SUCCESS_COUNT = 6 -USER_BREAKER_DELAY_TIME = 5000 - -# Consul -CONSUL_ADDRESS=http://127.0.0.1 -CONSUL_PORT=8500 -CONSUL_REGISTER_NAME=user -CONSUL_REGISTER_ETO=false -CONSUL_REGISTER_SERVICE_ADDRESS=127.0.0.1 -CONSUL_REGISTER_SERVICE_PORT=8099 -CONSUL_REGISTER_CHECK_NAME=user -CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099 -CONSUL_REGISTER_CHECK_INTERVAL=10 -CONSUL_REGISTER_CHECK_TIMEOUT=1 diff --git a/docker-compose.yml b/docker-compose.yml index 3e27f27d..c62ae0f7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,6 +33,7 @@ services: - "80:80" volumes: - ./:/var/www/swoft + env_file: ./.env.docker-compose stdin_open: true tty: true privileged: true From ac40ffd691349378a63f0d53047f277237a5b812 Mon Sep 17 00:00:00 2001 From: ccinn <471113744@qq.com> Date: Mon, 30 Jul 2018 21:44:13 +0800 Subject: [PATCH 260/643] transfer https://github.com/swoft-cloud/swoft-docker (https://github.com/swoft-cloud/swoft/pull/329) transfer to https://github.com/swoft-cloud/swoft-docker --- demo.sql | 16 --------------- docker-compose.yml | 50 ++++++++++------------------------------------ 2 files changed, 11 insertions(+), 55 deletions(-) delete mode 100644 demo.sql diff --git a/demo.sql b/demo.sql deleted file mode 100644 index 9f0c0def..00000000 --- a/demo.sql +++ /dev/null @@ -1,16 +0,0 @@ -CREATE DATABASE IF NOT EXISTS test; -USE test; -CREATE TABLE IF NOT EXISTS `user` ( - `id` INT (11) NOT NULL AUTO_INCREMENT, - `name` VARCHAR (20) DEFAULT NULL, - `sex` INT (1) NOT NULL DEFAULT '0', - `age` INT (1) NOT NULL DEFAULT '0', - `description` VARCHAR (240) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE = INNODB DEFAULT CHARSET = utf8 ; -CREATE TABLE `count` ( - `uid` INT (11) NOT NULL, - `fans` INT (1) NOT NULL DEFAULT '0', - `follows` INT (1) NOT NULL DEFAULT '0', - PRIMARY KEY (`uid`) -) ENGINE = INNODB DEFAULT CHARSET = utf8 ; diff --git a/docker-compose.yml b/docker-compose.yml index c62ae0f7..4049efd5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,42 +1,14 @@ version: '3' -networks: - swoft-network: - driver: bridge -services: - redis: - container_name: docker-compose-swoft-redis - image: redis:latest - privileged: true - ports: - - "6379:6379" - networks: - - swoft-network - - mysql: - container_name: docker-compose-swoft-mysql - image: mysql:5.6 - privileged: true - ports: - - "3306:3306" - volumes: - - ./demo.sql:/docker-entrypoint-initdb.d/demo.sql - environment: - MYSQL_ROOT_PASSWORD: 123456 - networks: - - swoft-network +services: swoft: - container_name: docker-compose-swoft - image: swoft/swoft:latest -# build: ./ - ports: - - "80:80" - volumes: - - ./:/var/www/swoft - env_file: ./.env.docker-compose - stdin_open: true - tty: true - privileged: true - command: php /var/www/swoft/bin/swoft start - networks: - - swoft-network \ No newline at end of file + image: swoft/swoft:latest +# build: ./ + ports: + - "80:80" + volumes: + - ./:/var/www/swoft + stdin_open: true + tty: true + privileged: true + command: php /var/www/swoft/bin/swoft start \ No newline at end of file From cac6a7a13ea7b9ff70ea90d0d3616ece2c2159c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Thu, 9 Aug 2018 16:25:32 +0800 Subject: [PATCH 261/643] Update Dockerfile Upgrade swoole version to 4.0.3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4d4d8e32..faefadc4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ MAINTAINER huangzhhui # Version ENV PHPREDIS_VERSION 4.0.0 ENV HIREDIS_VERSION 0.13.3 -ENV SWOOLE_VERSION 4.0.1 +ENV SWOOLE_VERSION 4.0.3 # Timezone RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ From 84930267f49990ef15081d208c9ff8fc9b75ec1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Thu, 9 Aug 2018 18:18:19 +0800 Subject: [PATCH 262/643] =?UTF-8?q?=E5=A2=9E=E5=8A=A0rpc-client=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=20(#340)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/server.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/server.php b/config/server.php index 0f25c2bc..48eb7b41 100644 --- a/config/server.php +++ b/config/server.php @@ -24,6 +24,12 @@ 'open_eof_check' => env('TCP_OPEN_EOF_CHECK', false), 'open_eof_split' => env('TCP_OPEN_EOF_SPLIT', true), 'package_eof' => "\r\n", + 'client' => [ + 'package_max_length' => env('TCP_CLIENT_PACKAGE_MAX_LENGTH', 1024 * 1024 * 2), + 'open_eof_check' => env('TCP_CLIENT_OPEN_EOF_CHECK', false), + 'open_eof_split' => env('TCP_CLIENT_OPEN_EOF_SPLIT', true), + 'package_eof' => "\r\n", + ], ], 'http' => [ 'host' => env('HTTP_HOST', '0.0.0.0'), From a11300c670e273afe89a3bee62386544ed3a68cc Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 22 Aug 2018 11:14:56 +0800 Subject: [PATCH 263/643] Dockerfile: replace CMD to ENTRYPOINT (#352) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index faefadc4..df93f8e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -80,4 +80,4 @@ RUN composer install --no-dev \ EXPOSE 80 -CMD ["php", "/var/www/swoft/bin/swoft", "start"] +ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "start"] From 836691353354c6594864ada104ac8da657d95b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sun, 26 Aug 2018 16:33:30 +0800 Subject: [PATCH 264/643] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 5c17ff85..cbfbd1a5 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "repositories": { "packagist": { "type": "composer", - "url": "https://packagist.phpcomposer.com/" + "url": "https://packagist.laravel-china.org/" } } } From b1536f2740520a7efe99a6d94072e90251a44c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sun, 26 Aug 2018 16:33:45 +0800 Subject: [PATCH 265/643] Update dev.composer.json --- dev.composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev.composer.json b/dev.composer.json index 51ef7ec4..605367e0 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -65,7 +65,7 @@ }, { "type": "composer", - "url": "https://packagist.phpcomposer.com/" + "url": "https://packagist.laravel-china.org/" } ] } From 8f7b5f5c0827d67b5164e98fdc524c27ca1909f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sun, 2 Sep 2018 23:19:48 +0800 Subject: [PATCH 266/643] Update changelog.md --- changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/changelog.md b/changelog.md index 84fb4b71..e7e64f64 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Change log +# 2018-08-15 +* 增加自定义组件支持 + +# 2018-08-07 +* 适配 Swoole 4.0.3 协程API变更 +* 加强 RpcClient 的自定义配置的能力 + # 2018-04-05 * 添加 websocket 支持 From 653d7d8c2fe386832ebed426b5e39e020617cc0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Sun, 2 Sep 2018 23:21:02 +0800 Subject: [PATCH 267/643] Update changelog.md --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index e7e64f64..7ea1dddc 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ # 2018-08-07 * 适配 Swoole 4.0.3 协程API变更 * 加强 RpcClient 的自定义配置的能力 +* 完善 Redis 的命令支持 # 2018-04-05 From 4df614a368bdd30c54a48ed19e0eb60dc17ef40a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=93=AD=E6=98=95?= <715557344@qq.com> Date: Wed, 5 Sep 2018 14:52:31 +0800 Subject: [PATCH 268/643] =?UTF-8?q?=E4=BF=AE=E6=94=B9pfile=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=BD=8D=E7=BD=AE=20(https://github.com/swoft-cloud/s?= =?UTF-8?q?woft/pull/375)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持别名配置 --- .env.example | 2 +- config/server.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 6981ef25..74c94abe 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,7 @@ LOG_ENABLE=false APP_DEBUG=false # Server -PFILE=/tmp/swoft.pid +PFILE=@runtime/swoft.pid PNAME=php-swoft TCPABLE=true CRONABLE=false diff --git a/config/server.php b/config/server.php index 48eb7b41..abc674ea 100644 --- a/config/server.php +++ b/config/server.php @@ -9,7 +9,7 @@ return [ 'server' => [ - 'pfile' => env('PFILE', '/tmp/swoft.pid'), + 'pfile' => alias(env('PFILE', '@runtime/swoft.pid')), 'pname' => env('PNAME', 'php-swoft'), 'tcpable' => env('TCPABLE', true), 'cronable' => env('CRONABLE', false), From ec8e65f3f489d82176eecc8c994c906295cf4191 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 12 Sep 2018 11:53:54 +0800 Subject: [PATCH 269/643] fix: cannot override Dockerfile startup command (#385) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4049efd5..c912a751 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,4 +11,4 @@ services: stdin_open: true tty: true privileged: true - command: php /var/www/swoft/bin/swoft start \ No newline at end of file + entrypoint: ["php", "/var/www/swoft/bin/swoft", "start"] From a8f97a81e5743cd1b4b82e9a5cc129f411b9f2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Fri, 14 Sep 2018 23:47:28 +0800 Subject: [PATCH 270/643] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index cbfbd1a5..4056611f 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "swoole", "swoft" ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", "license": "Apache-2.0", "require": { "php": ">=7.0", From 05a048180716d896f7ed450ef19ab8dcf8561bc7 Mon Sep 17 00:00:00 2001 From: SAMUEL NELA Date: Wed, 19 Sep 2018 04:11:36 +0200 Subject: [PATCH 271/643] Defined class as abstract (#390) --- test/Cases/AbstractTestCase.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php index f29c922d..848ac0dd 100644 --- a/test/Cases/AbstractTestCase.php +++ b/test/Cases/AbstractTestCase.php @@ -23,7 +23,7 @@ * * @package Swoft\Test\Cases */ -class AbstractTestCase extends TestCase +abstract class AbstractTestCase extends TestCase { const ACCEPT_VIEW = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'; const ACCEPT_JSON = 'application/json'; @@ -184,4 +184,4 @@ protected function buildMockRequest( $swooleRequest->get = array_merge($urlParams, $get); } } -} \ No newline at end of file +} From 7461231ee532e14856c18938793a7e418aee9a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Mon, 1 Oct 2018 16:36:02 +0800 Subject: [PATCH 272/643] Update README.md --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 87cfd78d..cb982fa9 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. - Base on Swoole extension -- Built-in HTTP, TCP, WebSocket Server -- Poweful AOP (Aspect Oriented Programming) +- Built-in HTTP, TCP, WebSocket Coroutine Server +- Powerful AOP (Aspect Oriented Programming) - Flexible and comprehensive annotations framework - Global dependency injection container - PSR-7 based HTTP message implementation @@ -28,34 +28,34 @@ The first high-performance PHP coroutine full-stack componentization framework b - PSR-15 based middleware - PSR-16 based cache design - Scalable high performance RPC -- Great service governance, fallback, load balance, service registration and discovery +- Holistic service governance, fallback, load balance, service registration and discovery - Database ORM -- Universal connection pool +- Universal connection pools - Mysql, Redis, RPC, HTTP Coroutine Clients - Coroutine driver client and blocking driver client seamlessly switch automatically - Coroutine and asynchronous task delivery -- Custom user process -- RESTful support -- Internationalization (i18n) support +- Custom user processes +- RESTful supported +- Internationalization (i18n) supported - High performance router - Fast and flexible parameter validator - Alias mechanism - Powerful log component -- Cross-platform application auto-reload +- Cross-platform application auto-reload mechanism ## Document [**Chinese Document**](https://doc.swoft.org) -[**English Document**](https://doc.swoft.org) Not yet, please help us write it. +[**English Document**](https://doc.swoft.org) Not yet, please help us to complete it. -QQ Group1: 548173319(Full) +QQ Group1: 548173319 QQ Group2: 778656850 ## Environmental Requirements 1. PHP 7.0 + -2. [Swoole 2.1.3](https://github.com/swoole/swoole-src/releases) +, *coroutine* and *async redis client* options are required +2. [Swoole 2.1.3](https://github.com/swoole/swoole-src/releases) + ( >= 4.1 is better), *coroutine* and *async redis client* options are required 3. [Hiredis](https://github.com/redis/hiredis/releases) 4. [Composer](https://getcomposer.org/) @@ -81,7 +81,7 @@ QQ Group2: 778656850 ## Configuration -If automatically copied `.env` file fails when `composer install` was executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that `composer update` will not trigger related copy operations. +If automatically copied `.env` file operation fails when `composer install` was executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that `composer update` will not trigger related copy operations. ``` # Server From df5839b3f29e50cc4008e04b8281964d2f41b947 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 24 Oct 2018 13:24:58 +0800 Subject: [PATCH 273/643] add custom function file (#418) * add custom function file * Update dev.composer.json * Create functions.php * Rename functions.php to Functions.php * Update composer.json * Update dev.composer.json --- app/Helper/Functions.php | 12 ++++++++++++ composer.json | 3 ++- dev.composer.json | 3 ++- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 app/Helper/Functions.php diff --git a/app/Helper/Functions.php b/app/Helper/Functions.php new file mode 100644 index 00000000..aa2e8e7a --- /dev/null +++ b/app/Helper/Functions.php @@ -0,0 +1,12 @@ + Date: Wed, 31 Oct 2018 18:28:59 +0800 Subject: [PATCH 274/643] Update ExceptionController.php (#433) spelling mistake. --- app/Controllers/ExceptionController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php index c4de3dba..ba12d69f 100644 --- a/app/Controllers/ExceptionController.php +++ b/app/Controllers/ExceptionController.php @@ -25,7 +25,7 @@ class ExceptionController * @RequestMapping() * @throws \Exception */ - public function exceptioin() + public function exception() { throw new \Exception('this is exception'); } @@ -56,4 +56,4 @@ public function viewException() { throw new BadMethodCallException('view exception! '); } -} \ No newline at end of file +} From 71bd72fb0d8b2c339d40cb648eb98a2a2e6cee40 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 15 Dec 2018 09:55:35 +0800 Subject: [PATCH 275/643] fix: exclude scann app/Helpler (#495) --- config/properties/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/properties/app.php b/config/properties/app.php index 31ee66e9..13b07361 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -17,7 +17,7 @@ 'App\Boot', ], 'excludeScan' => [ - + 'App\Helper', ], 'I18n' => [ 'sourceLanguage' => '@root/resources/messages/', From 1f8b791094207fa43921c39bc3426032364a5234 Mon Sep 17 00:00:00 2001 From: Wfuren Date: Sat, 15 Dec 2018 16:59:55 +0800 Subject: [PATCH 276/643] Update app.php (#497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复I18n默认指向目录错误 --- config/properties/app.php | 2 +- resources/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/properties/app.php b/config/properties/app.php index 13b07361..9a8aa450 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -20,7 +20,7 @@ 'App\Helper', ], 'I18n' => [ - 'sourceLanguage' => '@root/resources/messages/', + 'sourceLanguage' => '@resources/languages/', ], 'db' => require __DIR__ . DS . 'db.php', 'cache' => require __DIR__ . DS . 'cache.php', diff --git a/resources/README.md b/resources/README.md index e2323396..3e92d63d 100644 --- a/resources/README.md +++ b/resources/README.md @@ -1,4 +1,4 @@ # 资源目录 - views - 视图资源 -- messages - 翻译资源 \ No newline at end of file +- languages - 翻译资源 From 189c62e39a8cd5fd7f374eec5baf2e1d2a9f0783 Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 28 Dec 2018 10:42:32 +0800 Subject: [PATCH 277/643] fix:lang config is error (#523) --- config/properties/app.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/properties/app.php b/config/properties/app.php index 9a8aa450..605d0d87 100644 --- a/config/properties/app.php +++ b/config/properties/app.php @@ -19,8 +19,8 @@ 'excludeScan' => [ 'App\Helper', ], - 'I18n' => [ - 'sourceLanguage' => '@resources/languages/', + 'translator' => [ + 'languageDir' => '@resources/languages/', ], 'db' => require __DIR__ . DS . 'db.php', 'cache' => require __DIR__ . DS . 'cache.php', From 7dc5773c9f3613abe16c567d8c961baf83e2e396 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 5 Jan 2019 12:10:34 +0800 Subject: [PATCH 278/643] add --- .env.example | 114 ++++++++++++++++++++++++++++++++++ .github/CODE_OF_CONDUCT.md | 12 ++++ .github/ISSUE_TEMPLATE.md | 23 +++++++ .gitignore | 14 +++++ app/Application.php | 18 ++++++ app/AutoLoader.php | 23 +++++++ app/Helper/Functions.php | 2 + app/Model/Dao/DemoDao.php | 13 ++++ app/Model/Data/DemoData.php | 19 ++++++ app/Model/Logic/DemoLogic.php | 19 ++++++ app/bean.php | 4 ++ bin/bootstrap.php | 12 ++++ bin/swoft | 11 ++++ composer.json | 80 ++++++++++++++++++++++++ config/base.php | 4 ++ config/db.php | 4 ++ 16 files changed, 372 insertions(+) create mode 100644 .env.example create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .gitignore create mode 100644 app/Application.php create mode 100644 app/AutoLoader.php create mode 100644 app/Helper/Functions.php create mode 100644 app/Model/Dao/DemoDao.php create mode 100644 app/Model/Data/DemoData.php create mode 100644 app/Model/Logic/DemoLogic.php create mode 100644 app/bean.php create mode 100644 bin/bootstrap.php create mode 100644 bin/swoft create mode 100644 composer.json create mode 100644 config/base.php create mode 100644 config/db.php diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..74c94abe --- /dev/null +++ b/.env.example @@ -0,0 +1,114 @@ +# Application +TIME_ZONE=Asia/Shanghai +LOG_ENABLE=false +APP_DEBUG=false + +# Server +PFILE=@runtime/swoft.pid +PNAME=php-swoft +TCPABLE=true +CRONABLE=false +AUTO_RELOAD=true +AUTO_REGISTER=false + +# HTTP +HTTP_HOST=0.0.0.0 +HTTP_PORT=80 +HTTP_MODE=SWOOLE_PROCESS +HTTP_TYPE=SWOOLE_SOCK_TCP + +# WebSocket +WS_ENABLE_HTTP=true + +# TCP +TCP_HOST=0.0.0.0 +TCP_PORT=8099 +TCP_MODE=SWOOLE_PROCESS +TCP_TYPE=SWOOLE_SOCK_TCP +TCP_PACKAGE_MAX_LENGTH=2048 +TCP_OPEN_EOF_CHECK=false + +# Crontab +CRONTAB_TASK_COUNT=1024 +CRONTAB_TASK_QUEUE=2048 + +# Swoole Settings +WORKER_NUM=1 +MAX_REQUEST=100000 +DAEMONIZE=0 +DISPATCH_MODE=2 +TASK_IPC_MODE=1 +MESSAGE_QUEUE_KEY=1879052289 +TASK_TMPDIR=/tmp/ +LOG_FILE=@runtime/logs/swoole.log +TASK_WORKER_NUM=1 +PACKAGE_MAX_LENGTH=2048 +OPEN_HTTP2_PROTOCOL=false +SSL_CERT_FILE=/path/to/ssl_cert_file +SSL_KEY_FILE=/path/to/ssl_key_file + +# Database Master nodes +DB_NAME=dbMaster +DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8 +DB_MIN_ACTIVE=5 +DB_MAX_ACTIVE=10 +DB_MAX_WAIT=20 +DB_MAX_WAIT_TIME=3 +DB_MAX_IDLE_TIME=60 +DB_TIMEOUT=2 + +# Database Slave nodes +DB_SLAVE_NAME=dbSlave +DB_SLAVE_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8 +DB_SLAVE_MIN_ACTIVE=5 +DB_SLAVE_MAX_ACTIVE=10 +DB_SLAVE_MAX_WAIT=20 +DB_SLAVE_MAX_WAIT_TIME=3 +DB_SLAVE_MAX_IDLE_TIME=60 +DB_SLAVE_TIMEOUT=3 + +# Redis +REDIS_NAME=redis +REDIS_DB=2 +REDIS_URI=127.0.0.1:6379,127.0.0.1:6379 +REDIS_MIN_ACTIVE=5 +REDIS_MAX_ACTIVE=10 +REDIS_MAX_WAIT=20 +REDIS_MAX_WAIT_TIME=3 +REDIS_MAX_IDLE_TIME=60 +REDIS_TIMEOUT=3 +REDIS_SERIALIZE=1 + +# other redis node +REDIS_DEMO_REDIS_DB=6 +REDIS_DEMO_REDIS_PREFIX=demo_redis_ + +# User service (demo service) +USER_POOL_NAME=user +USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099 +USER_POOL_MIN_ACTIVE=5 +USER_POOL_MAX_ACTIVE=10 +USER_POOL_MAX_WAIT=20 +USER_POOL_TIMEOUT=200 +USER_POOL_MAX_WAIT_TIME=3 +USER_POOL_MAX_IDLE_TIME=60 +USER_POOL_USE_PROVIDER=false +USER_POOL_BALANCER=random +USER_POOL_PROVIDER=consul + +# User service breaker (demo service) +USER_BREAKER_FAIL_COUNT = 3 +USER_BREAKER_SUCCESS_COUNT = 6 +USER_BREAKER_DELAY_TIME = 5000 + +# Consul +CONSUL_ADDRESS=http://127.0.0.1 +CONSUL_PORT=8500 +CONSUL_REGISTER_NAME=user +CONSUL_REGISTER_ETO=false +CONSUL_REGISTER_SERVICE_ADDRESS=127.0.0.1 +CONSUL_REGISTER_SERVICE_PORT=8099 +CONSUL_REGISTER_CHECK_NAME=user +CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099 +CONSUL_REGISTER_CHECK_INTERVAL=10 +CONSUL_REGISTER_CHECK_TIMEOUT=1 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..c2a23273 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,12 @@ +Contributor Code of Conduct +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. + +This Code of Conduct is adapted from the Contributor Covenant, version 1.0.0, available at http://contributor-covenant.org/version/1/0/0/ \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..be97a863 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,23 @@ +| Q | A +| ------------------- | ----- +| Bug report? | yes/no +| Feature request? | yes/no +| Swoft version | x.y.z +| Swoole version | x.y.z (by `php --ri swoole`) +| PHP version | x.y.z (by `php -v`) +| Runtime environment | Win10/Mac/CentOS 7/Ubuntu/Docker etc. + +**Details** + +> Describe what you are trying to achieve and what goes wrong. + +```php +// paste output here +``` + +> Provide minimal script to reproduce the issue + +```php +// paste code +``` + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..610d543f --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.buildpath +.settings/ +.project +*.patch +.idea/ +.git/ +runtime/ +vendor/ +temp/ +*.lock +.phpintel/ +.env +.DS_Store +public/devtool/ \ No newline at end of file diff --git a/app/Application.php b/app/Application.php new file mode 100644 index 00000000..2d01ddb4 --- /dev/null +++ b/app/Application.php @@ -0,0 +1,18 @@ + __DIR__, + ]; + } +} \ No newline at end of file diff --git a/app/Helper/Functions.php b/app/Helper/Functions.php new file mode 100644 index 00000000..a4abe2da --- /dev/null +++ b/app/Helper/Functions.php @@ -0,0 +1,2 @@ +run(); \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..ab882bb6 --- /dev/null +++ b/composer.json @@ -0,0 +1,80 @@ +{ + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", + "license": "Apache-2.0", + "require": { + "php": ">=7.1", + "swoft/core": "dev-master", + "swoft/annotation": "dev-master", + "swoft/bean": "dev-master", + "swoft/event": "dev-master", + "swoft/aop": "dev-master", + "swoft/config": "dev-master", + "swoft/stdlib": "dev-master", + "swoft/log": "dev-master" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^5.7", + "friendsofphp/php-cs-fixer": "^2.10", + "psy/psysh": "@stable" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Helper/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + } + }, + "scripts": { + }, + "repositories": [ + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-core.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-annotation.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-bean.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-event.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-aop.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-config.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-log.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-stdlib.git" + }, + { + "type": "composer", + "url": "https://packagist.laravel-china.org/" + } + ] +} \ No newline at end of file diff --git a/config/base.php b/config/base.php new file mode 100644 index 00000000..05e0b10e --- /dev/null +++ b/config/base.php @@ -0,0 +1,4 @@ + Date: Mon, 7 Jan 2019 00:43:01 +0800 Subject: [PATCH 279/643] add test --- app/Model/Logic/DemoLogic.php | 2 +- config/base.php | 2 +- config/db.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Model/Logic/DemoLogic.php b/app/Model/Logic/DemoLogic.php index 6847d247..0d73f263 100644 --- a/app/Model/Logic/DemoLogic.php +++ b/app/Model/Logic/DemoLogic.php @@ -2,8 +2,8 @@ namespace App\Model\Logic; -use Swoft\Bean\Annotation\Mapping\Bean; use App\Model\Data\DemoData; +use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Bean\Annotation\Mapping\Inject; /** diff --git a/config/base.php b/config/base.php index 05e0b10e..5ac7cee8 100644 --- a/config/base.php +++ b/config/base.php @@ -1,4 +1,4 @@ 'stelin' ]; \ No newline at end of file diff --git a/config/db.php b/config/db.php index 05e0b10e..f03cc1a8 100644 --- a/config/db.php +++ b/config/db.php @@ -1,4 +1,4 @@ 'host' ]; \ No newline at end of file From 563db3eb4e8e92912ab4eef058b5422618494c4a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 10 Jan 2019 17:20:35 +0800 Subject: [PATCH 280/643] parse object definition by annotation and definition --- app/Model/Logic/DemoLogic.php | 53 ++++++++++++++++++++++++++++++++++- app/bean.php | 9 +++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/app/Model/Logic/DemoLogic.php b/app/Model/Logic/DemoLogic.php index 0d73f263..f9e05048 100644 --- a/app/Model/Logic/DemoLogic.php +++ b/app/Model/Logic/DemoLogic.php @@ -7,7 +7,7 @@ use Swoft\Bean\Annotation\Mapping\Inject; /** - * @Bean() + * @Bean(alias="deloc") */ class DemoLogic { @@ -16,4 +16,55 @@ class DemoLogic * @var DemoData */ private $data; + + /** + * @var string + */ + private $definitionData; + + /** + * @var string + */ + private $name; + + /** + * @var int + */ + private $type = 0; + + /** + * @var DemoData + */ + private $construnctData; + + /** + * DemoLogic constructor. + * + * @param string $name + * @param string $type + * @param DemoData + */ + public function __construct(string $name, string $type, DemoData $data) + { + $this->name = $name; + $this->type = $type; + + $this->construnctData = $data; + } + + /** + * @return DemoData + */ + public function getData(): DemoData + { + return $this->data; + } + + /** + * @return string + */ + public function getDefinitionData(): string + { + return $this->definitionData; + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 05e0b10e..d7201790 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,4 +1,11 @@ [ + [ + 'dDname', + 12, + '${\App\Model\Data\DemoData}' + ], + 'definitionData' => 'definitionData...' + ] ]; \ No newline at end of file From 730bf18fa220ac985eab46aa705cf640a7fa1b20 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 13 Jan 2019 18:56:38 +0800 Subject: [PATCH 281/643] config and bean --- app/Model/Dao/DemoDao.php | 4 ++++ app/Model/Data/DemoData.php | 6 ++++++ app/Model/Logic/DemoLogic.php | 21 ++++++++++++++++++++- app/bean.php | 2 +- config/base.php | 2 +- config/db.php | 2 +- 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/Model/Dao/DemoDao.php b/app/Model/Dao/DemoDao.php index 92ae4771..379f8904 100644 --- a/app/Model/Dao/DemoDao.php +++ b/app/Model/Dao/DemoDao.php @@ -10,4 +10,8 @@ class DemoDao { + public function get() + { + echo 'dao get' . PHP_EOL; + } } \ No newline at end of file diff --git a/app/Model/Data/DemoData.php b/app/Model/Data/DemoData.php index 02201737..862572e2 100644 --- a/app/Model/Data/DemoData.php +++ b/app/Model/Data/DemoData.php @@ -16,4 +16,10 @@ class DemoData * @var DemoDao */ private $dao; + + public function getDao() + { + echo 'data getDao'.PHP_EOL; + $this->dao->get(); + } } \ No newline at end of file diff --git a/app/Model/Logic/DemoLogic.php b/app/Model/Logic/DemoLogic.php index f9e05048..e4d8c2a6 100644 --- a/app/Model/Logic/DemoLogic.php +++ b/app/Model/Logic/DemoLogic.php @@ -5,6 +5,7 @@ use App\Model\Data\DemoData; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Bean\Annotation\Mapping\Inject; +use Swoft\Config\Annotation\Config; /** * @Bean(alias="deloc") @@ -37,6 +38,18 @@ class DemoLogic */ private $construnctData; + /** + * @Config("name") + * @var string + */ + private $configName = ''; + + /** + * @Config("db.host") + * @var string + */ + private $configHost = ''; + /** * DemoLogic constructor. * @@ -57,7 +70,13 @@ public function __construct(string $name, string $type, DemoData $data) */ public function getData(): DemoData { - return $this->data; + $this->data->getDao(); + echo 'name=' . $this->name . PHP_EOL; + echo 'type=' . $this->type . PHP_EOL; + echo 'configName=' . $this->configName . PHP_EOL; + echo 'configHost=' . $this->configHost . PHP_EOL; + + return $this->construnctData; } /** diff --git a/app/bean.php b/app/bean.php index d7201790..3aa9a161 100644 --- a/app/bean.php +++ b/app/bean.php @@ -4,7 +4,7 @@ [ 'dDname', 12, - '${\App\Model\Data\DemoData}' + '${App\Model\Data\DemoData}' ], 'definitionData' => 'definitionData...' ] diff --git a/config/base.php b/config/base.php index 5ac7cee8..7b8e33db 100644 --- a/config/base.php +++ b/config/base.php @@ -1,4 +1,4 @@ 'stelin' + 'name' => 'swoft framework 2.0' ]; \ No newline at end of file diff --git a/config/db.php b/config/db.php index f03cc1a8..35b17338 100644 --- a/config/db.php +++ b/config/db.php @@ -1,4 +1,4 @@ 'host' + 'host' => 'http://127.0.0.0.1' ]; \ No newline at end of file From 32c8dba758cb6bff6e71e07ff96cebb372c7d134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Wed, 16 Jan 2019 19:11:48 +0800 Subject: [PATCH 282/643] Set enable_static_handler default value to false --- config/server.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/server.php b/config/server.php index abc674ea..f399161f 100644 --- a/config/server.php +++ b/config/server.php @@ -57,7 +57,7 @@ 'package_max_length' => env('PACKAGE_MAX_LENGTH', 2048), 'upload_tmp_dir' => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'), 'document_root' => env('DOCUMENT_ROOT', BASE_PATH . '/public'), - 'enable_static_handler' => env('ENABLE_STATIC_HANDLER', true), + 'enable_static_handler' => env('ENABLE_STATIC_HANDLER', false), 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), 'ssl_cert_file' => env('SSL_CERT_FILE', ''), 'ssl_key_file' => env('SSL_KEY_FILE', ''), From 56bf61c7f916782c78ff7a83374491d5fec29459 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 19 Jan 2019 17:05:47 +0800 Subject: [PATCH 283/643] mv core to framework --- app/Application.php | 2 +- bin/bootstrap.php | 3 +++ composer.json | 32 +++++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/Application.php b/app/Application.php index 2d01ddb4..90779854 100644 --- a/app/Application.php +++ b/app/Application.php @@ -3,7 +3,7 @@ namespace App; -use Swoft\Core\SwoftApplication; +use Swoft\SwoftApplication; /** * Application diff --git a/bin/bootstrap.php b/bin/bootstrap.php index 360efb96..fd237ed0 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -8,5 +8,8 @@ // Project config path !defined('CONFIG_PATH') && define('CONFIG_PATH', BASE_PATH . '/config'); +// Project runtime path +!defined('RUNTIME_PATH') && define('RUNTIME_PATH', BASE_PATH . '/runtime'); + // Composer autoload require_once BASE_PATH . '/vendor/autoload.php'; \ No newline at end of file diff --git a/composer.json b/composer.json index ab882bb6..afdb32e3 100644 --- a/composer.json +++ b/composer.json @@ -17,11 +17,17 @@ "swoft/aop": "dev-master", "swoft/config": "dev-master", "swoft/stdlib": "dev-master", + "swoft/framework": "2.0.x-dev", + "swoft/http-message": "2.0.x-dev", + "swoft/server": "dev-master", + "swoft/tcp-server": "dev-master", + "swoft/http-server": "2.0.x-dev", + "swoft/websocket-server": "2.0.x-dev", "swoft/log": "dev-master" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^5.7", + "phpunit/phpunit": "^7.5", "friendsofphp/php-cs-fixer": "^2.10", "psy/psysh": "@stable" }, @@ -72,6 +78,30 @@ "type": "git", "url": "git@github.com:swoft-cloud/swoft-stdlib.git" }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-framework.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-http-message.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-server.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-tcp-server.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-http-server.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-websocket-server.git" + }, { "type": "composer", "url": "https://packagist.laravel-china.org/" From bdfa2a90488b69662ddcf48b7f8279c3eab80e9b Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 21 Jan 2019 14:46:34 +0800 Subject: [PATCH 284/643] add file --- app/Controller/TestController.php | 16 ++++++++++++++++ bin/test.php | 17 +++++++++++++++++ composer.json | 3 +++ 3 files changed, 36 insertions(+) create mode 100644 app/Controller/TestController.php create mode 100644 bin/test.php diff --git a/app/Controller/TestController.php b/app/Controller/TestController.php new file mode 100644 index 00000000..52799e51 --- /dev/null +++ b/app/Controller/TestController.php @@ -0,0 +1,16 @@ +=7.1", + "psr/http-message": "^1.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0", "swoft/core": "dev-master", "swoft/annotation": "dev-master", "swoft/bean": "dev-master", From ecafbe485319cc8b7fb51911d249cf8f697b79ff Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 21 Jan 2019 17:23:11 +0800 Subject: [PATCH 285/643] modify composer.json --- composer.json | 82 ++++++++++----------------------------------------- 1 file changed, 15 insertions(+), 67 deletions(-) diff --git a/composer.json b/composer.json index 097e7ad2..e588cb13 100644 --- a/composer.json +++ b/composer.json @@ -13,20 +13,20 @@ "psr/http-message": "^1.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", - "swoft/core": "dev-master", - "swoft/annotation": "dev-master", - "swoft/bean": "dev-master", - "swoft/event": "dev-master", - "swoft/aop": "dev-master", - "swoft/config": "dev-master", - "swoft/stdlib": "dev-master", - "swoft/framework": "2.0.x-dev", - "swoft/http-message": "2.0.x-dev", - "swoft/server": "dev-master", - "swoft/tcp-server": "dev-master", - "swoft/http-server": "2.0.x-dev", - "swoft/websocket-server": "2.0.x-dev", - "swoft/log": "dev-master" + "swoft/annotation": "^2.0", + "swoft/bean": "^2.0", + "swoft/event": "^2.0", + "swoft/aop": "^2.0", + "swoft/config": "^2.0", + "swoft/stdlib": "^2.0", + "swoft/framework": "^2.0", + "swoft/http-message": "^2.0", + "swoft/server": "^2.0", + "swoft/tcp-server": "^2.0", + "swoft/http-server": "^2.0", + "swoft/websocket-server": "^2.0", + "swoft/log": "^2.0", + "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", @@ -51,59 +51,7 @@ "repositories": [ { "type": "git", - "url": "git@github.com:swoft-cloud/swoft-core.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-annotation.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-bean.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-event.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-aop.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-config.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-log.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-stdlib.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-framework.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-http-message.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-server.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-tcp-server.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-http-server.git" - }, - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-websocket-server.git" + "url": "git@github.com:swoft-cloud/swoft-component.git" }, { "type": "composer", From bb24552b6e8dd12f223929f8022253e5c6422cc5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 22 Jan 2019 16:26:26 +0800 Subject: [PATCH 286/643] add --- bin/swoft | 4 +--- bin/test.php | 19 +++++++++---------- composer.json | 3 --- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/bin/swoft b/bin/swoft index 99e0cfef..c2db65b6 100644 --- a/bin/swoft +++ b/bin/swoft @@ -1,7 +1,5 @@ #!/usr/bin/env php -__proxyCall('Swoft\\Tcp\\Server\\Swoole\\ConnectListener', 'onConnect', func_get_args()); } - } -echo Test::getAb(); \ No newline at end of file + diff --git a/composer.json b/composer.json index e588cb13..bd67a946 100644 --- a/composer.json +++ b/composer.json @@ -10,9 +10,6 @@ "license": "Apache-2.0", "require": { "php": ">=7.1", - "psr/http-message": "^1.0", - "psr/http-server-handler": "^1.0", - "psr/http-server-middleware": "^1.0", "swoft/annotation": "^2.0", "swoft/bean": "^2.0", "swoft/event": "^2.0", From dc1ebc9311bc543e2d12fd5f2fbb82c66270b786 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 23 Jan 2019 17:53:44 +0800 Subject: [PATCH 287/643] add tsting --- app/Aspect/LogAspect.php | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 app/Aspect/LogAspect.php diff --git a/app/Aspect/LogAspect.php b/app/Aspect/LogAspect.php new file mode 100644 index 00000000..511625e2 --- /dev/null +++ b/app/Aspect/LogAspect.php @@ -0,0 +1,78 @@ +test .= ' before1 '; + } + + /** + * @After() + */ + public function after() + { + $this->test .= ' after1 '; + } + + /** + * @AfterReturning() + */ + public function afterReturn(JoinPoint $joinPoint) + { + $result = $joinPoint->getReturn(); + return $result . ' afterReturn1 '; + } + + /** + * @Around() + * @param ProceedingJoinPoint $proceedingJoinPoint + * + * @return mixed + */ + public function around(ProceedingJoinPoint $proceedingJoinPoint) + { + $this->test .= ' around-before1 '; + $result = $proceedingJoinPoint->proceed(); + $this->test .= ' around-after1 '; + return $result . $this->test; + } + + /** + * @AfterThrowing() + */ + public function afterThrowing() + { + echo "aop=1 afterThrowing !\n"; + } +} \ No newline at end of file From b81859f34b0544245625def962ba4f3bc0c2825a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 24 Jan 2019 16:40:58 +0800 Subject: [PATCH 288/643] add aop --- app/Aspect/LogAspect.php | 19 ++++++++----------- app/Model/Logic/DemoLogic.php | 8 +++++--- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/Aspect/LogAspect.php b/app/Aspect/LogAspect.php index 511625e2..e87e8f86 100644 --- a/app/Aspect/LogAspect.php +++ b/app/Aspect/LogAspect.php @@ -24,17 +24,12 @@ */ class LogAspect { - /** - * @var - */ - private $test; - /** * @Before() */ public function before() { - $this->test .= ' before1 '; + echo ' before1 ' . PHP_EOL; } /** @@ -42,7 +37,7 @@ public function before() */ public function after() { - $this->test .= ' after1 '; + echo ' after ' . PHP_EOL; } /** @@ -51,7 +46,9 @@ public function after() public function afterReturn(JoinPoint $joinPoint) { $result = $joinPoint->getReturn(); - return $result . ' afterReturn1 '; + echo ' afterReturn ' . PHP_EOL; + + return $result . ' afterReturn1'; } /** @@ -62,9 +59,9 @@ public function afterReturn(JoinPoint $joinPoint) */ public function around(ProceedingJoinPoint $proceedingJoinPoint) { - $this->test .= ' around-before1 '; - $result = $proceedingJoinPoint->proceed(); - $this->test .= ' around-after1 '; + echo ' around-before1 ' . PHP_EOL; + $result = $proceedingJoinPoint->proceed(); + echo ' around-after1 ' . PHP_EOL; return $result . $this->test; } diff --git a/app/Model/Logic/DemoLogic.php b/app/Model/Logic/DemoLogic.php index e4d8c2a6..595ebb06 100644 --- a/app/Model/Logic/DemoLogic.php +++ b/app/Model/Logic/DemoLogic.php @@ -66,9 +66,9 @@ public function __construct(string $name, string $type, DemoData $data) } /** - * @return DemoData + * @return string */ - public function getData(): DemoData + public function getData(): string { $this->data->getDao(); echo 'name=' . $this->name . PHP_EOL; @@ -76,7 +76,9 @@ public function getData(): DemoData echo 'configName=' . $this->configName . PHP_EOL; echo 'configHost=' . $this->configHost . PHP_EOL; - return $this->construnctData; + var_dump($this->construnctData); + + return 'do demo logic'; } /** From f909c5765947b46824686f81f15142847049b51c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 24 Jan 2019 19:24:14 +0800 Subject: [PATCH 289/643] modify aop --- app/Aspect/LogAspect.php | 20 ++++----- app/Aspect/LogAspect2.php | 76 +++++++++++++++++++++++++++++++++++ app/Aspect/TestLog.php | 24 +++++++++++ app/Model/Logic/DemoLogic.php | 6 +-- 4 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 app/Aspect/LogAspect2.php create mode 100644 app/Aspect/TestLog.php diff --git a/app/Aspect/LogAspect.php b/app/Aspect/LogAspect.php index e87e8f86..7812d81e 100644 --- a/app/Aspect/LogAspect.php +++ b/app/Aspect/LogAspect.php @@ -16,9 +16,9 @@ /** * Class LogAspect * - * @Aspect() + * @Aspect(1) * @PointBean( - * include={"deloc"} + * include={"testLog"} * ) * @since 2.0 */ @@ -29,7 +29,7 @@ class LogAspect */ public function before() { - echo ' before1 ' . PHP_EOL; + echo 'apsect1 before' . PHP_EOL; } /** @@ -37,7 +37,7 @@ public function before() */ public function after() { - echo ' after ' . PHP_EOL; + echo 'apsect1 after' . PHP_EOL; } /** @@ -46,9 +46,9 @@ public function after() public function afterReturn(JoinPoint $joinPoint) { $result = $joinPoint->getReturn(); - echo ' afterReturn ' . PHP_EOL; + echo 'apsect1 afterReturn ' . PHP_EOL; - return $result . ' afterReturn1'; + return 'new value afterReturn '; } /** @@ -59,10 +59,10 @@ public function afterReturn(JoinPoint $joinPoint) */ public function around(ProceedingJoinPoint $proceedingJoinPoint) { - echo ' around-before1 ' . PHP_EOL; + echo 'apsect1 around before ' . PHP_EOL; $result = $proceedingJoinPoint->proceed(); - echo ' around-after1 ' . PHP_EOL; - return $result . $this->test; + echo 'apsect1 around after ' . PHP_EOL; + return $result; } /** @@ -70,6 +70,6 @@ public function around(ProceedingJoinPoint $proceedingJoinPoint) */ public function afterThrowing() { - echo "aop=1 afterThrowing !\n"; + echo "apsect1 afterThrowing !\n"; } } \ No newline at end of file diff --git a/app/Aspect/LogAspect2.php b/app/Aspect/LogAspect2.php new file mode 100644 index 00000000..7877f722 --- /dev/null +++ b/app/Aspect/LogAspect2.php @@ -0,0 +1,76 @@ +getReturn(); + echo 'apsect2 afterReturn ' . PHP_EOL; + + return 'new value afterReturn2 '; + } + + /** + * @Around() + * @param ProceedingJoinPoint $proceedingJoinPoint + * + * @return mixed + */ + public function around(ProceedingJoinPoint $proceedingJoinPoint) + { + echo 'apsect2 around before ' . PHP_EOL; + $result = $proceedingJoinPoint->proceed(); + echo 'apsect2 around after ' . PHP_EOL; + return $result; + } + + /** + * @AfterThrowing() + */ + public function afterThrowing() + { + echo "apsect2 afterThrowing !\n"; + } +} \ No newline at end of file diff --git a/app/Aspect/TestLog.php b/app/Aspect/TestLog.php new file mode 100644 index 00000000..4fea6fed --- /dev/null +++ b/app/Aspect/TestLog.php @@ -0,0 +1,24 @@ +data->getDao(); echo 'name=' . $this->name . PHP_EOL; @@ -76,9 +76,7 @@ public function getData(): string echo 'configName=' . $this->configName . PHP_EOL; echo 'configHost=' . $this->configHost . PHP_EOL; - var_dump($this->construnctData); - - return 'do demo logic'; + return $this->construnctData; } /** From ffb02ff1e8ea56b07bf7aad62d028c4b270a17a2 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 25 Jan 2019 14:19:17 +0800 Subject: [PATCH 290/643] add --- app/Aspect/TestLog.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Aspect/TestLog.php b/app/Aspect/TestLog.php index 4fea6fed..4d42bc8d 100644 --- a/app/Aspect/TestLog.php +++ b/app/Aspect/TestLog.php @@ -18,7 +18,7 @@ class TestLog public function log() { - throw new Exception('11'); +// throw new Exception('11'); echo 'test log' . PHP_EOL; } } \ No newline at end of file From 30b2159b8f413ca241070f27d7c1ecafefd0a73b Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 28 Jan 2019 18:02:37 +0800 Subject: [PATCH 291/643] add log --- app/Aspect/TestLog.php | 1 - bin/test.php | 21 ++++++++++++--------- changelog.md | 4 ++++ 3 files changed, 16 insertions(+), 10 deletions(-) create mode 100644 changelog.md diff --git a/app/Aspect/TestLog.php b/app/Aspect/TestLog.php index 4d42bc8d..49e85922 100644 --- a/app/Aspect/TestLog.php +++ b/app/Aspect/TestLog.php @@ -3,7 +3,6 @@ namespace App\Aspect; -use mysql_xdevapi\Exception; use Swoft\Bean\Annotation\Mapping\Bean; /** diff --git a/bin/test.php b/bin/test.php index ea94a5b3..fb5be992 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,16 +1,19 @@ __proxyCall('Swoft\\Tcp\\Server\\Swoole\\ConnectListener', 'onConnect', func_get_args()); - } + } diff --git a/changelog.md b/changelog.md new file mode 100644 index 00000000..91829aca --- /dev/null +++ b/changelog.md @@ -0,0 +1,4 @@ +# change log + +1. 新增对象池 +2. prototype优化,clone \ No newline at end of file From 7e3006159c3c016b0a8f0046b2e965201a939165 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 31 Jan 2019 00:08:46 +0800 Subject: [PATCH 292/643] add controller --- app/Controller/TestController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Controller/TestController.php b/app/Controller/TestController.php index 52799e51..e29a8c69 100644 --- a/app/Controller/TestController.php +++ b/app/Controller/TestController.php @@ -12,5 +12,8 @@ */ class TestController { - + public function test() + { + return 'swoft framework'; + } } \ No newline at end of file From fda609adf07ed2351540075a01d8ddb264af8c71 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 2 Feb 2019 21:54:37 +0800 Subject: [PATCH 293/643] add composer --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index bd67a946..0a23b873 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,8 @@ "swoft/http-server": "^2.0", "swoft/websocket-server": "^2.0", "swoft/log": "^2.0", + "swoft/db": "^2.0", + "swoft/connection-pool": "^2.0", "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { From 0595668e853cf5ae55000d182cdbd60a0ef2b4d0 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 18 Feb 2019 22:13:33 +0800 Subject: [PATCH 294/643] modify composer --- bin/test.php | 19 ------------------- composer.json | 2 ++ 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 bin/test.php diff --git a/bin/test.php b/bin/test.php deleted file mode 100644 index fb5be992..00000000 --- a/bin/test.php +++ /dev/null @@ -1,19 +0,0 @@ -=7.1", + "ext-pdo": "*", + "ext-json": "*", "swoft/annotation": "^2.0", "swoft/bean": "^2.0", "swoft/event": "^2.0", From 3bc5da9ecfb7e8c1e123747e131733ea33938cb0 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 23 Feb 2019 15:10:36 +0800 Subject: [PATCH 295/643] add console --- composer.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composer.json b/composer.json index 638427a1..df338583 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,8 @@ "swoft/log": "^2.0", "swoft/db": "^2.0", "swoft/connection-pool": "^2.0", + "swoft/test": "^2.0", + "swoft/console": "^2.0", "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { From b80430d3cc5131052b37b356977e839186bf39fa Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 26 Feb 2019 17:16:12 +0800 Subject: [PATCH 296/643] add --- app/Controller/TestController.php | 2 +- app/Model/Logic/DemoLogic.php | 4 +- bin/bootstrap.php | 14 +- bin/phpunit | 69122 ++++++++++++++++++++++++++++ composer.json | 4 +- 5 files changed, 69128 insertions(+), 18 deletions(-) create mode 100755 bin/phpunit diff --git a/app/Controller/TestController.php b/app/Controller/TestController.php index e29a8c69..aad9f122 100644 --- a/app/Controller/TestController.php +++ b/app/Controller/TestController.php @@ -14,6 +14,6 @@ class TestController { public function test() { - return 'swoft framework'; + return 'swoft framework hello'; } } \ No newline at end of file diff --git a/app/Model/Logic/DemoLogic.php b/app/Model/Logic/DemoLogic.php index 061debdc..9435a4d5 100644 --- a/app/Model/Logic/DemoLogic.php +++ b/app/Model/Logic/DemoLogic.php @@ -5,7 +5,7 @@ use App\Model\Data\DemoData; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Bean\Annotation\Mapping\Inject; -use Swoft\Config\Annotation\Config; +use Swoft\Config\Annotation\Mapping\Config; /** * @Bean(alias="deloc") @@ -57,7 +57,7 @@ class DemoLogic * @param string $type * @param DemoData */ - public function __construct(string $name, string $type, DemoData $data) + public function __construct(string $name = '', string $type = '', DemoData $data = null) { $this->name = $name; $this->type = $type; diff --git a/bin/bootstrap.php b/bin/bootstrap.php index fd237ed0..b1d05993 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -1,15 +1,3 @@ ')) { + fwrite( + STDERR, + sprintf( + 'PHPUnit 7.5.6 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . + 'This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . + 'You are using PHP %s (%s).' . PHP_EOL, + PHP_VERSION, + PHP_BINARY + ) + ); + + die(1); +} + +if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { + $execute = true; +} else { + $execute = false; +} + +$options = getopt('', array('prepend:', 'manifest')); + +if (isset($options['prepend'])) { + require $options['prepend']; +} + +if (isset($options['manifest'])) { + $printManifest = true; +} + +unset($options); + +define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__)); +define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-7.5.6.phar'); + +Phar::mapPhar('phpunit-7.5.6.phar'); + +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/DeepCopy.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Filter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php'; +require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php'; +require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Assert.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SelfDescribing.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/AssertionFailedError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/CodeCoverageException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Constraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ArrayHasKey.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ArraySubset.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Composite.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Attribute.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Callback.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ClassHasAttribute.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ClassHasStaticAttribute.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Count.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/DirectoryExists.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ExceptionCode.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ExceptionMessage.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ExceptionMessageRegularExpression.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/FileExists.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/GreaterThan.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsAnything.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsEmpty.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsEqual.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsFalse.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsFinite.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsIdentical.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsInfinite.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsInstanceOf.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsJson.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsNan.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsNull.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsReadable.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsTrue.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsType.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsWritable.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/JsonMatches.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LessThan.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalAnd.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalNot.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalOr.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalXor.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ObjectHasAttribute.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/RegularExpression.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/SameSize.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringContains.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringEndsWith.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringMatchesFormatDescription.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringStartsWith.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/TraversableContains.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/TraversableContainsOnly.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/RiskyTest.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/RiskyTestError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/CoveredCodeNotExecutedException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Test.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestSuite.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/DataProviderTestSuite.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Error.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Deprecated.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Notice.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Warning.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/ExceptionWrapper.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/ExpectationFailedException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/IncompleteTest.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestCase.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/IncompleteTestCase.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/IncompleteTestError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/InvalidCoversTargetException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MissingCoversAnnotationException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Exception/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/Identity.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/Stub.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/Match.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/ParametersMatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/InvocationMocker.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/NamespaceMatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Generator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invocation/Invocation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/MatcherCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Verifiable.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invokable.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/InvocationMocker.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invocation/StaticInvocation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invocation/ObjectInvocation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/Invocation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedRecorder.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/AnyInvokedCount.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/StatelessInvocation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/AnyParameters.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/ConsecutiveParameters.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/DeferredError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtIndex.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtLeastCount.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtMostCount.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedCount.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/MethodName.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/Parameters.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockBuilder.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockMethod.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockMethodSet.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockObject.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/ForwardCompatibility/MockObject.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Exception/RuntimeException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnArgument.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnCallback.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnReference.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnSelf.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnStub.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/OutputError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTest.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTestCase.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTestError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTestSuiteError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SyntheticError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestFailure.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestListener.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestListenerDefaultImplementation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestResult.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestSuiteIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/UnintentionallyCoveredCodeError.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Warning.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/WarningTestCase.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/Hook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/TestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterIncompleteTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterLastTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterRiskyTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterSkippedTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestErrorHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestFailureHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestWarningHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/BaseTestRunner.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/BeforeFirstTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/BeforeTestHook.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/GroupFilterIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/Factory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/NameFilterIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestResultCacheInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/NullTestResultCache.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/PhptTestCase.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/ResultCacheExtension.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/TestSuiteLoader.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/StandardTestSuiteLoader.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/TestListenerAdapter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestResultCache.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/TestSuiteSorter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Version.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/TextUI/Command.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Printer.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/TextUI/ResultPrinter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/TextUI/TestRunner.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Blacklist.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Configuration.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/ConfigurationGenerator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/ErrorHandler.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/FileLoader.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Filesystem.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Filter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Getopt.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/GlobalState.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/InvalidArgumentHelper.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Json.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Log/JUnit.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Log/TeamCity.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/PHP/AbstractPhpProcess.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/PHP/DefaultPhpProcess.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/PHP/WindowsPhpProcess.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/RegularExpression.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Test.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/CliTestDoxPrinter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/ResultPrinter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/HtmlResultPrinter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/NamePrettifier.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/TestResult.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/TextResultPrinter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/XmlResultPrinter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TextTestListRenderer.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Type.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/XdebugFilterScriptGenerator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Xml.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/XmlTestListRenderer.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-token-stream/Token.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-token-stream/Token/Stream.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-token-stream/Token/Stream/CachingFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Type.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Application.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/ApplicationName.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Author.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/AuthorCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/AuthorCollectionIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ManifestElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/AuthorElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ElementCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/AuthorElementCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/BundledComponent.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/BundledComponentCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/BundledComponentCollectionIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/BundlesElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ComponentElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ComponentElementCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ContainsElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/CopyrightElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/CopyrightInformation.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Email.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ExtElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ExtElementCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Extension.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ExtensionElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/InvalidApplicationNameException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/InvalidEmailException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/InvalidUrlException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Library.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/License.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/LicenseElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Manifest.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ManifestDocument.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestDocumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ManifestDocumentLoadingException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/ManifestDocumentMapper.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestElementException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/ManifestLoader.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestLoaderException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/ManifestSerializer.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/PhpElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Requirement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/PhpExtensionRequirement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/PhpVersionRequirement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/RequirementCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/RequirementCollectionIterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/RequiresElement.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Url.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/VersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/AbstractVersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/AndVersionConstraintGroup.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/AnyVersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/ExactVersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/InvalidVersionException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/OrVersionConstraintGroup.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/PreReleaseSuffix.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/SpecificMajorVersionConstraint.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/Version.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/VersionConstraintParser.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/VersionConstraintValue.php'; +require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/VersionNumber.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Call/Call.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Call/CallCenter.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/Comparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/Factory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Comparator/Factory.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ArrayComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ObjectComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Doubler.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophet.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Util/ExportUtil.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Util/StringUtil.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/CodeCoverage.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/RuntimeException.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/CoveredCodeNotExecutedException.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Driver/Driver.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Driver/PHPDBG.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Driver/Xdebug.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Filter.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/MissingCoversAnnotationException.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/AbstractNode.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/Builder.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/Directory.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/File.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/Iterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Clover.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Crap4j.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer/Dashboard.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer/Directory.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Facade.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer/File.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/PHP.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Text.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/BuildInformation.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Coverage.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Node.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Directory.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Facade.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/File.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Method.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Project.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Report.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Source.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Tests.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Totals.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Unit.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Util.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Version.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-code-unit-reverse-lookup/Wizard.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ComparisonFailure.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/DOMNodeComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/DateTimeComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ScalarComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/NumericComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/DoubleComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ExceptionComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/MockObjectComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ResourceComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/SplObjectStorageComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/TypeComparator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Chunk.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Exception/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Exception/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Exception/ConfigurationException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Diff.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Differ.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Line.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/LongestCommonSubsequenceCalculator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/DiffOutputBuilderInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/AbstractChunkOutputBuilder.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/DiffOnlyOutputBuilder.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Parser.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-environment/Console.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-environment/OperatingSystem.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-environment/Runtime.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-exporter/Exporter.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-file-iterator/Facade.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-file-iterator/Factory.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-file-iterator/Iterator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/Blacklist.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/CodeExporter.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/exceptions/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/Restorer.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/exceptions/RuntimeException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/Snapshot.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-invoker/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-invoker/Invoker.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-invoker/TimeoutException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-enumerator/Enumerator.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-enumerator/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-enumerator/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-reflector/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-reflector/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-reflector/ObjectReflector.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-recursion-context/Context.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-recursion-context/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-recursion-context/InvalidArgumentException.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-resource-operations/ResourceOperations.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-timer/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-timer/RuntimeException.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-timer/Timer.php'; +require 'phar://phpunit-7.5.6.phar' . '/sebastian-version/Version.php'; +require 'phar://phpunit-7.5.6.phar' . '/php-text-template/Template.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/Exception.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/NamespaceUri.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/NamespaceUriException.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/Token.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/TokenCollection.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/TokenCollectionException.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/Tokenizer.php'; +require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/XMLSerializer.php'; +require 'phar://phpunit-7.5.6.phar' . '/webmozart-assert/Assert.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Description.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tag.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Element.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/File.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Fqsen.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/FqsenResolver.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Location.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Project.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/ProjectFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Type.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/TypeResolver.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Array_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Boolean.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Callable_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Compound.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Context.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/ContextFactory.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Float_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Integer.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Iterable_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Mixed_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Null_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Nullable.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Object_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Parent_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Resource_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Scalar.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Self_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Static_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/String_.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/This.php'; +require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Void_.php'; + +if ($execute) { + if (isset($printManifest)) { + print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); + + exit; + } + + unset($execute); + + PHPUnit\TextUI\Command::main(); +} + +__HALT_COMPILER(); ?> +|�vphpunit-7.5.6.phar-phpdocumentor-type-resolver/FqsenResolver.php �yj\ �R�ٶ$phpdocumentor-type-resolver/Type.php��yj\���[L�/phpdocumentor-type-resolver/Types/Resource_.php��yj\�Nf ��-phpdocumentor-type-resolver/Types/Context.phpV �yj\V �{��,phpdocumentor-type-resolver/Types/Scalar.php�yj\U����-phpdocumentor-type-resolver/Types/Boolean.php��yj\��f��-phpdocumentor-type-resolver/Types/String_.php��yj\����-phpdocumentor-type-resolver/Types/Parent_.php4�yj\4��j�+phpdocumentor-type-resolver/Types/Self_.php�yj\��9'�.phpdocumentor-type-resolver/Types/Nullable.php��yj\��Ab��.phpdocumentor-type-resolver/Types/Compound.php �yj\ ���|�/phpdocumentor-type-resolver/Types/Iterable_.php��yj\�O��Ӷ+phpdocumentor-type-resolver/Types/Void_.phpW�yj\W��ֶ/phpdocumentor-type-resolver/Types/Callable_.php��yj\�4ɿ�,phpdocumentor-type-resolver/Types/Mixed_.php��yj\�ϯ�ʶ-phpdocumentor-type-resolver/Types/Object_.php��yj\���Q��,phpdocumentor-type-resolver/Types/Float_.php��yj\��w,�-phpdocumentor-type-resolver/Types/Integer.php��yj\�"s��*phpdocumentor-type-resolver/Types/This.php��yj\��h²�,phpdocumentor-type-resolver/Types/Array_.php-�yj\-��_�-phpdocumentor-type-resolver/Types/Static_.phpU�yj\U�ޟ �4phpdocumentor-type-resolver/Types/ContextFactory.php��yj\�E�3�+phpdocumentor-type-resolver/Types/Null_.php��yj\�@�%��,phpdocumentor-type-resolver/TypeResolver.phpw$�yj\w$U��y�#phpdocumentor-type-resolver/LICENSE8�yj\8��ʶobject-reflector/LICENSE +�yj\ +�F���Hphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php�yj\�:�F�Bphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php��yj\���$϶Hphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php)�yj\)F��4�Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php��yj\�h+Ephpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php��yj\�77/%�Jphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php��yj\������Jphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php��yj\����?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php��yj\�z�F��Dphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php��yj\�ۉ?�Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php��yj\��X�@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php��yj\��Z^�Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.phpD�yj\D�p��1phpspec-prophecy/Prophecy/Exception/Exception.php+�yj\+����@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php��yj\��g��Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php�yj\;��Pphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php�yj\� ƶFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php��yj\�2T�ѶKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php,�yj\,���a�Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php��yj\��l<��Lphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpJ�yj\J~��D�Dphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php��yj\����5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php��yj\��5D�5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.phpq1�yj\q1�?�8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpH�yj\H�gZ��8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php,�yj\,�W�/phpspec-prophecy/Prophecy/Prophecy/Revealer.php��yj\�j�ɸ�?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php��yj\�i��8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php4 �yj\4 A;K2�:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php,�yj\,cR�̶<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php��yj\� N�v�<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php�yj\��r�Aphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php��yj\�pb���<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php� �yj\� �3��6phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php��yj\��n�\�Bphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php��yj\�#I��;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php��yj\��bN/�@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php��yj\������<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php��yj\��4�̶<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php��yj\��J�:�;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php��yj\�ٰ���@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php�yj\��|�:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php��yj\�F�h�=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php9 +�yj\9 +E��.�Cphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php��yj\�����<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php��yj\��?Br�?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php�yj\�5�H�>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php��yj\�<�[�Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php��yj\�|��Aphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php��yj\�t�>˶Bphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php�yj\T�H�;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.phpf�yj\fei�-phpspec-prophecy/Prophecy/Doubler/Doubler.php��yj\�8]�^�3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php��yj\�̇g�3phpspec-prophecy/Prophecy/Doubler/NameGenerator.php��yj\���7�5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php��yj\�8d�j�Hphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php��yj\�:0`�Cphpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php��yj\�x��^�Dphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phpl�yj\l)�5:�Ephpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php �yj\ ���Z�=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php$ �yj\$ �AA��Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php �yj\ ��jN�?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.phpa �yj\a �����Pphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpp�yj\px����Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php~ �yj\~ ��T��?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php +�yj\ +_aGk�0phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpF �yj\F ��l��&phpspec-prophecy/Prophecy/Argument.php��yj\�AT�;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.phps�yj\s��hǶ0phpspec-prophecy/Prophecy/Comparator/Factory.php��yj\�ֈi��:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpK�yj\K)RQ�<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php� �yj\� X���7phpspec-prophecy/Prophecy/Prediction/CallPrediction.phpQ �yj\Q I��;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php��yj\�Vb{ζ:phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php��yj\�L9%�<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php��yj\�`IE�%phpspec-prophecy/Prophecy/Prophet.phpb�yj\b�Z`�Iphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.phpx�yj\xrЬ�=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.phpD�yj\Dd9�϶Gphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php��yj\� �;��Cphpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.phpo�yj\ou9��-phpspec-prophecy/Prophecy/Util/ExportUtil.phpP�yj\P2�qƶ-phpspec-prophecy/Prophecy/Util/StringUtil.php� +�yj\� +�Z~��-phpspec-prophecy/Prophecy/Call/CallCenter.php*�yj\*�.w�'phpspec-prophecy/Prophecy/Call/Call.php� �yj\� 8Vuٶ3phpspec-prophecy/Prophecy/Promise/ReturnPromise.php�yj\�؏�6phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpK�yj\K����5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php��yj\�[���;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'�yj\'�(���2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php] �yj\] �� ~�phpspec-prophecy/LICENSE}�yj\}��6�,sebastian-comparator/ExceptionComparator.php��yj\�ѕ���*sebastian-comparator/NumericComparator.php��yj\�=��Զ-sebastian-comparator/MockObjectComparator.phpP�yj\P�PN�'sebastian-comparator/TypeComparator.php��yj\�S��Ҷ)sebastian-comparator/DoubleComparator.phpt�yj\t�^k��#sebastian-comparator/Comparator.php\�yj\\8�9�(sebastian-comparator/ArrayComparator.php��yj\�Lj:Q�*sebastian-comparator/ComparisonFailure.php� �yj\� �I�+sebastian-comparator/ResourceComparator.php.�yj\.�K�� sebastian-comparator/Factory.php �yj\ o���*sebastian-comparator/DOMNodeComparator.php� +�yj\� +8���3sebastian-comparator/SplObjectStorageComparator.php��yj\� ��)sebastian-comparator/ObjectComparator.php�yj\�9�2�sebastian-comparator/LICENSE �yj\ �q��+sebastian-comparator/DateTimeComparator.php� �yj\� �~�ȶ)sebastian-comparator/ScalarComparator.php� �yj\� QM�theseer-tokenizer/Token.phpq�yj\q��?��#theseer-tokenizer/XMLSerializer.php� �yj\� E!{A�theseer-tokenizer/Exception.phpg�yj\gլ�X�%theseer-tokenizer/TokenCollection.phpr +�yj\r +�g9�+theseer-tokenizer/NamespaceUriException.phpr�yj\r+��A�.theseer-tokenizer/TokenCollectionException.phpu�yj\u��BK�theseer-tokenizer/Tokenizer.php��yj\��� ��theseer-tokenizer/LICENSE��yj\��R (�"theseer-tokenizer/NamespaceUri.phpw�yj\w 'U��'sebastian-recursion-context/Context.php{�yj\{����)sebastian-recursion-context/Exception.phpJ�yj\J����8sebastian-recursion-context/InvalidArgumentException.php��yj\�mH�#sebastian-recursion-context/LICENSE�yj\ ��>�php-text-template/Template.php� �yj\� �w4��php-text-template/LICENSE �yj\ S�:��object-enumerator/LICENSE�yj\�f�ζphpunit/Runner/Version.php��yj\�O�W��"phpunit/Runner/TestSuiteSorter.phpT*�yj\T*� ���*phpunit/Runner/StandardTestSuiteLoader.php �yj\ �\�'phpunit/Runner/ResultCacheExtension.php� �yj\� 9��9�&phpunit/Runner/Hook/BeforeTestHook.php��yj\�z�ݶ+phpunit/Runner/Hook/TestListenerAdapter.php��yj\����;�*phpunit/Runner/Hook/AfterTestErrorHook.php��yj\�LX�Ƕ,phpunit/Runner/Hook/AfterTestFailureHook.php��yj\�z�c��)phpunit/Runner/Hook/AfterLastTestHook.phpw�yj\w�~F�+phpunit/Runner/Hook/BeforeFirstTestHook.php{�yj\{�.�phpunit/Runner/Hook/Hook.php+�yj\+kXM��*phpunit/Runner/Hook/AfterRiskyTestHook.php��yj\�jY�M�/phpunit/Runner/Hook/AfterSuccessfulTestHook.php��yj\�j3���,phpunit/Runner/Hook/AfterTestWarningHook.php��yj\���ֆ�,phpunit/Runner/Hook/AfterSkippedTestHook.php��yj\��J �/phpunit/Runner/Hook/AfterIncompleteTestHook.php��yj\��*K� phpunit/Runner/Hook/TestHook.php<�yj\<xᤸ�%phpunit/Runner/Hook/AfterTestHook.phpQ�yj\Q����phpunit/Runner/Exception.phpK�yj\K@�5v�"phpunit/Runner/TestSuiteLoader.php�yj\XVE�phpunit/Runner/PhptTestCase.php>�yj\>fE�4phpunit/Runner/Filter/IncludeGroupFilterIterator.php��yj\�dL� �,phpunit/Runner/Filter/NameFilterIterator.php� �yj\� $��7�!phpunit/Runner/Filter/Factory.php��yj\���I��-phpunit/Runner/Filter/GroupFilterIterator.php �yj\ !��=�4phpunit/Runner/Filter/ExcludeGroupFilterIterator.php��yj\�,�8�!phpunit/Runner/BaseTestRunner.phpc�yj\c���&�phpunit/Exception.phpD�yj\D.�`�phpunit/TextUI/Command.php.��yj\.�TvD]�phpunit/TextUI/TestRunner.php���yj\��UC=� phpunit/TextUI/ResultPrinter.phpF=�yj\F=���!�7phpunit/Framework/TestListenerDefaultImplementation.phpn�yj\n"�� �2phpunit/Framework/InvalidCoversTargetException.phpG�yj\G�iZ��!phpunit/Framework/SkippedTest.php�yj\p��y�!phpunit/Framework/OutputError.php5�yj\5D���*phpunit/Framework/AssertionFailedError.php�yj\�9A��6phpunit/Framework/MissingCoversAnnotationException.phpD�yj\D�2l+�$phpunit/Framework/RiskyTestError.phpM�yj\Mie�"phpunit/Framework/Error/Notice.phpJ�yj\J�[�b�&phpunit/Framework/Error/Deprecated.phpN�yj\N,`,��!phpunit/Framework/Error/Error.phpa�yj\a��VԶ#phpunit/Framework/Error/Warning.phpK�yj\K]�W�"phpunit/Framework/TestListener.php��yj\�J �$phpunit/Framework/IncompleteTest.php��yj\��%�t�$phpunit/Framework/SyntheticError.php��yj\� ��p�)phpunit/Framework/IncompleteTestError.phpW�yj\WzY}+�phpunit/Framework/Assert.php!b�yj\!b�wt��phpunit/Framework/Exception.php �yj\ �K���%phpunit/Framework/SkippedTestCase.php��yj\�JH�phpunit/Framework/Test.php�yj\δP'�,phpunit/Framework/Constraint/JsonMatches.phpm �yj\m �BIw�,phpunit/Framework/Constraint/GreaterThan.php��yj\�?�2�)phpunit/Framework/Constraint/IsFinite.php�yj\M�_m�+phpunit/Framework/Constraint/IsReadable.phpE�yj\EX��T�+phpunit/Framework/Constraint/LogicalXor.php� �yj\� ��0�?phpunit/Framework/Constraint/StringMatchesFormatDescription.php` +�yj\` +�z���+phpunit/Framework/Constraint/IsInfinite.php�yj\�,��'phpunit/Framework/Constraint/IsType.php� �yj\� .jj�,phpunit/Framework/Constraint/ArrayHasKey.php��yj\�F�.phpunit/Framework/Constraint/ExceptionCode.php8�yj\8�^*R�-phpunit/Framework/Constraint/IsInstanceOf.php��yj\�����)phpunit/Framework/Constraint/SameSize.php��yj\���D�8phpunit/Framework/Constraint/TraversableContainsOnly.php[ �yj\[ &�x��)phpunit/Framework/Constraint/Callback.php�yj\ ���1phpunit/Framework/Constraint/ExceptionMessage.php.�yj\.�vH�0phpunit/Framework/Constraint/DirectoryExists.phpK�yj\KqS�A�)phpunit/Framework/Constraint/LessThan.php��yj\��&4/�3phpunit/Framework/Constraint/ObjectHasAttribute.php]�yj\]��OH�2phpunit/Framework/Constraint/RegularExpression.php<�yj\<6���*phpunit/Framework/Constraint/Exception.php��yj\����ܶ4phpunit/Framework/Constraint/TraversableContains.php_ �yj\_ :��ж/phpunit/Framework/Constraint/StringEndsWith.phpP�yj\P���'�(phpunit/Framework/Constraint/IsEqual.php��yj\���s��,phpunit/Framework/Constraint/ArraySubset.php��yj\�^�S �*phpunit/Framework/Constraint/Attribute.php �yj\ �E�+phpunit/Framework/Constraint/IsWritable.phpE�yj\E?�}ζ(phpunit/Framework/Constraint/IsEmpty.php��yj\��%n��1phpunit/Framework/Constraint/StringStartsWith.php=�yj\=;��1�(phpunit/Framework/Constraint/IsFalse.php�yj\�M��*phpunit/Framework/Constraint/Composite.php��yj\��>+�+phpunit/Framework/Constraint/Constraint.php8�yj\8r�8�*phpunit/Framework/Constraint/LogicalOr.php� �yj\� ���t�Bphpunit/Framework/Constraint/ExceptionMessageRegularExpression.php^�yj\^�b�&phpunit/Framework/Constraint/IsNan.php �yj\ �s�,phpunit/Framework/Constraint/IsIdentical.phpW�yj\W�����8phpunit/Framework/Constraint/ClassHasStaticAttribute.php��yj\��v�j�&phpunit/Framework/Constraint/Count.php� �yj\� ���*�+phpunit/Framework/Constraint/IsAnything.php��yj\�hu~�'phpunit/Framework/Constraint/IsNull.php�yj\�n��2phpunit/Framework/Constraint/ClassHasAttribute.php��yj\�.Tpo�+phpunit/Framework/Constraint/LogicalNot.php��yj\��EzӶ@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php!�yj\!X�۶'phpunit/Framework/Constraint/IsTrue.php�yj\D�/phpunit/Framework/Constraint/StringContains.php��yj\���I�'phpunit/Framework/Constraint/IsJson.php��yj\�h��/�+phpunit/Framework/Constraint/FileExists.php<�yj\<i� �+phpunit/Framework/Constraint/LogicalAnd.php �yj\ <���phpunit/Framework/TestCase.php��yj\���Aphpunit/Framework/MockObject/Exception/BadMethodCallException.phpc�yj\cu���;phpunit/Framework/MockObject/Exception/RuntimeException.phpW�yj\W[���4phpunit/Framework/MockObject/Exception/Exception.phpe�yj\e]Q⺶@phpunit/Framework/MockObject/ForwardCompatibility/MockObject.php��yj\���pd�1phpunit/Framework/MockObject/InvocationMocker.php��yj\���3�%phpunit/Framework/MockObject/Stub.phpY�yj\YPTЃ�+phpunit/Framework/MockObject/Verifiable.php��yj\�eA��;phpunit/Framework/MockObject/Generator/wsdl_method.tpl.dist<�yj\<��i��;phpunit/Framework/MockObject/Generator/deprecation.tpl.dist;�yj\;O5�s�Cphpunit/Framework/MockObject/Generator/mocked_class_method.tpl.dist��yj\��d��:phpunit/Framework/MockObject/Generator/wsdl_class.tpl.dist��yj\�w&S�;phpunit/Framework/MockObject/Generator/trait_class.tpl.dist7�yj\7�[$~�>phpunit/Framework/MockObject/Generator/proxied_method.tpl.dist��yj\�~D���Cphpunit/Framework/MockObject/Generator/proxied_method_void.tpl.dist��yj\�� 5_�Bphpunit/Framework/MockObject/Generator/mocked_method_void.tpl.dist&�yj\&�����<phpunit/Framework/MockObject/Generator/mocked_class.tpl.dist��yj\��ʷ��=phpunit/Framework/MockObject/Generator/mocked_method.tpl.dist]�yj\]~ӭ^�Dphpunit/Framework/MockObject/Generator/mocked_static_method.tpl.dist��yj\�w�Q��<phpunit/Framework/MockObject/Generator/mocked_clone.tpl.dist��yj\��aT�>phpunit/Framework/MockObject/Generator/unmocked_clone.tpl.dist��yj\�8W}ض6phpunit/Framework/MockObject/Invocation/Invocation.php��yj\��ȼ��<phpunit/Framework/MockObject/Invocation/StaticInvocation.phpG�yj\G�r��<phpunit/Framework/MockObject/Invocation/ObjectInvocation.php��yj\� ϿS�+phpunit/Framework/MockObject/MockMethod.phpN*�yj\N*7!�k�*phpunit/Framework/MockObject/Invokable.php��yj\���- +�6phpunit/Framework/MockObject/Matcher/AnyParameters.php��yj\�:I|�;phpunit/Framework/MockObject/Matcher/InvokedAtLeastOnce.phpf�yj\fh(l�3phpunit/Framework/MockObject/Matcher/Parameters.php��yj\�ڋ޴�<phpunit/Framework/MockObject/Matcher/StatelessInvocation.phpU�yj\U�@��3phpunit/Framework/MockObject/Matcher/MethodName.php��yj\�>Ȯ�7phpunit/Framework/MockObject/Matcher/InvokedAtIndex.php �yj\ `/n�5phpunit/Framework/MockObject/Matcher/InvokedCount.php� +�yj\� +a��8phpunit/Framework/MockObject/Matcher/InvokedRecorder.php:�yj\:I.W�>phpunit/Framework/MockObject/Matcher/ConsecutiveParameters.php�yj\2*H�8phpunit/Framework/MockObject/Matcher/AnyInvokedCount.phpV�yj\V��7�3phpunit/Framework/MockObject/Matcher/Invocation.php��yj\�w>�<phpunit/Framework/MockObject/Matcher/InvokedAtLeastCount.php��yj\���J��6phpunit/Framework/MockObject/Matcher/DeferredError.php*�yj\*=F���;phpunit/Framework/MockObject/Matcher/InvokedAtMostCount.php��yj\�� ��,phpunit/Framework/MockObject/MockBuilder.phpg�yj\g� i|�4phpunit/Framework/MockObject/Stub/ReturnArgument.php��yj\��WS�6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php��yj\��a��7phpunit/Framework/MockObject/Stub/MatcherCollection.php��yj\�岛�/phpunit/Framework/MockObject/Stub/Exception.php��yj\��ϙĶ4phpunit/Framework/MockObject/Stub/ReturnValueMap.php��yj\�Z�6�5phpunit/Framework/MockObject/Stub/ReturnReference.php��yj\��g�M�0phpunit/Framework/MockObject/Stub/ReturnStub.php��yj\����4phpunit/Framework/MockObject/Stub/ReturnCallback.phpf�yj\f��Ӷ0phpunit/Framework/MockObject/Stub/ReturnSelf.php��yj\��r@��.phpunit/Framework/MockObject/MockMethodSet.php<�yj\<Y��Ͷ(phpunit/Framework/MockObject/Matcher.php3"�yj\3"*ɠ��9phpunit/Framework/MockObject/Builder/InvocationMocker.phpF�yj\F� $��-phpunit/Framework/MockObject/Builder/Stub.php��yj\���)��7phpunit/Framework/MockObject/Builder/NamespaceMatch.php �yj\ N�q;�1phpunit/Framework/MockObject/Builder/Identity.phpZ�yj\Z�8z9�8phpunit/Framework/MockObject/Builder/ParametersMatch.php��yj\����Ķ.phpunit/Framework/MockObject/Builder/Match.php��yj\���ɶ8phpunit/Framework/MockObject/Builder/MethodNameMatch.php�yj\�xf��*phpunit/Framework/MockObject/Generator.phpm{�yj\m{� 1ж+phpunit/Framework/MockObject/MockObject.php,�yj\,�5�S�+phpunit/Framework/SkippedTestSuiteError.phpV�yj\VoϺ��phpunit/Framework/RiskyTest.php�yj\8c�}�(phpunit/Framework/IncompleteTestCase.php��yj\�,4�'phpunit/Framework/TestSuiteIterator.php��yj\��u o�$phpunit/Framework/SelfDescribing.php��yj\��S� phpunit/Framework/TestResult.php�s�yj\�s���j�5phpunit/Framework/CoveredCodeNotExecutedException.phpC�yj\CI��o�+phpunit/Framework/CodeCoverageException.php4�yj\4ψͶ+phpunit/Framework/DataProviderTestSuite.phpV�yj\VI�:��%phpunit/Framework/WarningTestCase.php��yj\��l�&phpunit/Framework/Assert/Functions.php4��yj\4�Q�`.�phpunit/Framework/TestSuite.phpOi�yj\OiczN�&phpunit/Framework/ExceptionWrapper.php� �yj\� �78�phpunit/Framework/Warning.php�yj\.�;�&phpunit/Framework/SkippedTestError.phpQ�yj\Q�E�0phpunit/Framework/ExpectationFailedException.php*�yj\*�a�!phpunit/Framework/TestFailure.php� �yj\� hۼö5phpunit/Framework/UnintentionallyCoveredCodeError.php��yj\��|��phpunit/Util/Xml.php��yj\���j̶'phpunit/Util/ConfigurationGenerator.php��yj\�lv.��phpunit/Util/GlobalState.php��yj\���$�'phpunit/Util/PHP/AbstractPhpProcess.php'&�yj\'&�&bL�&phpunit/Util/PHP/WindowsPhpProcess.phpY�yj\Y���&phpunit/Util/PHP/DefaultPhpProcess.php��yj\�!��R�phpunit/Util/PHP/eval-stdin.php�yj\�^�߶0phpunit/Util/PHP/Template/TestCaseClass.tpl.dist �yj\ /�Y�1phpunit/Util/PHP/Template/TestCaseMethod.tpl.distZ �yj\Z ���F�/phpunit/Util/PHP/Template/PhptTestCase.tpl.distb�yj\b���̶"phpunit/Util/RegularExpression.php��yj\�qċ�phpunit/Util/Test.php���yj\��)�j-�phpunit/Util/ErrorHandler.phpO �yj\O ;m�phpunit/Util/Filter.php� �yj\� ����*phpunit/Util/TestDox/HtmlResultPrinter.phpz +�yj\z +�8� �'phpunit/Util/TestDox/NamePrettifier.php��yj\��ʲ��*phpunit/Util/TestDox/TextResultPrinter.php4�yj\4�E���*phpunit/Util/TestDox/CliTestDoxPrinter.php|/�yj\|/��)<�#phpunit/Util/TestDox/TestResult.php��yj\��\3��)phpunit/Util/TestDox/XmlResultPrinter.php��yj\��6ݷ�&phpunit/Util/TestDox/ResultPrinter.phpc�yj\c�l�c�&phpunit/Util/InvalidArgumentHelper.php��yj\������$phpunit/Util/XmlTestListRenderer.php� �yj\� r��Ŷphpunit/Util/Getopt.php��yj\��}ֶphpunit/Util/Blacklist.php;�yj\;�-ݶ,phpunit/Util/XdebugFilterScriptGenerator.php!�yj\!P�1ɶphpunit/Util/Type.php%�yj\%:� �$phpunit/Util/NullTestResultCache.phpU�yj\Uf����phpunit/Util/Printer.php� �yj\� �xV��phpunit/Util/Log/TeamCity.php�)�yj\�)_ +&��phpunit/Util/Log/JUnit.php`-�yj\`-�;߶)phpunit/Util/TestResultCacheInterface.php��yj\�Vί#�phpunit/Util/Configuration.phpz��yj\z�؃ֶphpunit/Util/Json.php� �yj\� ��)��%phpunit/Util/TextTestListRenderer.php��yj\��(4� phpunit/Util/TestResultCache.phpz�yj\za�s��phpunit/Util/FileLoader.phpK�yj\K�AΑ�phpunit/Util/Filesystem.phpm�yj\m�k���Rdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.phpd�yj\d���-�Rdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.phpm �yj\m ���Ldoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php��yj\��.�öEdoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php~�yj\~���:�<doctrine-instantiator/Doctrine/Instantiator/Instantiator.php�yj\��϶doctrine-instantiator/LICENSE$�yj\$ +͂�,phpdocumentor-reflection-common/Location.phpH�yj\H?-��+phpdocumentor-reflection-common/Element.php1�yj\1�iUҶ)phpdocumentor-reflection-common/Fqsen.php �yj\ ��Ӝ�(phpdocumentor-reflection-common/File.php7�yj\7�3"�'phpdocumentor-reflection-common/LICENSE9�yj\9*2Ȑ�+phpdocumentor-reflection-common/Project.php�yj\/H� �2phpdocumentor-reflection-common/ProjectFactory.php�yj\Q�"ܶ'sebastian-global-state/CodeExporter.phpf �yj\f |�!�#sebastian-global-state/Restorer.php"�yj\"M���#sebastian-global-state/Snapshot.php�!�yj\�!Ò���$sebastian-global-state/Blacklist.php� +�yj\� +ܫ9��6sebastian-global-state/exceptions/RuntimeException.php��yj\� �y˶/sebastian-global-state/exceptions/Exception.phpP�yj\PW�Z�sebastian-global-state/LICENSE�yj\q~Pd�:myclabs-deep-copy/DeepCopy/Exception/PropertyException.phpx�yj\x�4��7myclabs-deep-copy/DeepCopy/Exception/CloneException.php�yj\L��t�7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php �yj\ 7���4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php��yj\��-h�Amyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php�yj\GF�жGmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php�yj\J�霶Amyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php��yj\���̶;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php��yj\���Zٶ:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php~�yj\~:�̶6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php��yj\�s�wֶ:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php��yj\�Y��x�Dmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpo�yj\o?3�:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php&�yj\&��6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php��yj\�?S�b�.myclabs-deep-copy/DeepCopy/Matcher/Matcher.php��yj\�q�e�3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php��yj\�a�˲�0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php�yj\����Lmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php��yj\���7߶Gmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php�yj\�:D�Bmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php��yj\����,myclabs-deep-copy/DeepCopy/Filter/Filter.php\�yj\\6S���3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php��yj\�]��]�'myclabs-deep-copy/DeepCopy/DeepCopy.php��yj\�a���(myclabs-deep-copy/DeepCopy/deep_copy.php��yj\�"e��myclabs-deep-copy/LICENSE5�yj\5ʭ˄� manifest.txtf�yj\ff�p�(sebastian-object-reflector/Exception.phpN�yj\N� ^�7sebastian-object-reflector/InvalidArgumentException.php��yj\�Y�J�.sebastian-object-reflector/ObjectReflector.php��yj\�� "�php-invoker/Exception.php@�yj\@'��php-invoker/Invoker.php��yj\� �̶ php-invoker/TimeoutException.phpx�yj\xI-��php-timer/Timer.php��yj\���ܲ�php-timer/RuntimeException.php|�yj\|�t�S�php-timer/Exception.phpD�yj\D�ɶphp-timer/LICENSE�yj\�����-sebastian-code-unit-reverse-lookup/Wizard.phpe �yj\e ����*sebastian-code-unit-reverse-lookup/LICENSE�yj\XX��.phpdocumentor-reflection-docblock/DocBlock.php�yj\��Z�5phpdocumentor-reflection-docblock/DocBlockFactory.php�$�yj\�$�Ń��<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php��yj\��x��2phpdocumentor-reflection-docblock/DocBlock/Tag.phpu�yj\u⹰�Aphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.phpp�yj\p3��{�:phpdocumentor-reflection-docblock/DocBlock/Description.php/�yj\/>� �9phpdocumentor-reflection-docblock/DocBlock/Serializer.php��yj\�"~.0�9phpdocumentor-reflection-docblock/DocBlock/TagFactory.php�yj\P;Ͷ:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php��yj\��Ȉض:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.phpF�yj\F.h�)�:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.phpn �yj\n С���Aphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php� �yj\� s��ȶ;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php��yj\��@��;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.phpX +�yj\X +D� �;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php� �yj\� �\�>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php� +�yj\� +���Dphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.php��yj\���R�Hphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php��yj\��2i��Lphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php��yj\������Rphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php+�yj\+��ܶ;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php��yj\�X� +c�;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.phpp�yj\p�H���<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php� �yj\� ]]H��7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php� �yj\� y%��@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php� �yj\� � 2��:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php� �yj\� ����9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php� �yj\� �yaö8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.phpN�yj\NV���:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php��yj\�~�|��=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php��yj\�Dy7�9phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php|�yj\|�8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php �yj\ ��϶Aphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.phpM�yj\M���|�Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php��yj\���c�Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php,�yj\,�8(��8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.phpP�yj\PT,��Aphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.phpR.�yj\R.Ø��>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php!�yj\!��}�)phpdocumentor-reflection-docblock/LICENSE8�yj\8��ʶ3phar-io-manifest/xml/ComponentElementCollection.php�yj\#�Iζ9phar-io-manifest/xml/ManifestDocumentLoadingException.php��yj\����>�'phar-io-manifest/xml/LicenseElement.php4�yj\4�� �(phar-io-manifest/xml/RequiresElement.php�yj\(�ͷ�)phar-io-manifest/xml/CopyrightElement.php��yj\�����-phar-io-manifest/xml/ExtElementCollection.php�yj\����)phar-io-manifest/xml/ExtensionElement.phpB�yj\B����&phar-io-manifest/xml/AuthorElement.php7�yj\7���'phar-io-manifest/xml/BundlesElement.php&�yj\&Oϯ�)phar-io-manifest/xml/ComponentElement.php>�yj\> �+ȶ*phar-io-manifest/xml/ElementCollection.php��yj\��'�*�0phar-io-manifest/xml/AuthorElementCollection.php �yj\ B�yζ#phar-io-manifest/xml/PhpElement.php��yj\�p{:�#phar-io-manifest/xml/ExtElement.php��yj\�h/j3�(phar-io-manifest/xml/ManifestElement.php� �yj\� :6�X�)phar-io-manifest/xml/ManifestDocument.php� �yj\� ���1�(phar-io-manifest/xml/ContainsElement.php'�yj\'���Z�'phar-io-manifest/ManifestSerializer.php�yj\�tphar-io-manifest/values/BundledComponentCollectionIterator.php��yj\�}��'phar-io-manifest/values/Application.php��yj\�7$~׶,phar-io-manifest/values/BundledComponent.php��yj\��-�Ŷ1phar-io-manifest/values/RequirementCollection.php��yj\�)���#phar-io-manifest/values/License.php�yj\*�׶#phar-io-manifest/values/Library.php��yj\�F�z�'phar-io-manifest/values/Requirement.phpp�yj\p6V�A�"phar-io-manifest/values/Author.php%�yj\%��W8�1phar-io-manifest/values/PhpVersionRequirement.php�yj\�,��6phar-io-manifest/values/BundledComponentCollection.php(�yj\(� +�ڶ0phar-io-manifest/values/CopyrightInformation.phpx�yj\xt��b�%phar-io-manifest/values/Extension.php��yj\��0� phar-io-manifest/values/Type.php��yj\��pn��4phar-io-manifest/values/AuthorCollectionIterator.phpa�yj\a�����!phar-io-manifest/values/Email.php��yj\�c�M�9phar-io-manifest/values/RequirementCollectionIterator.php��yj\�n���,phar-io-manifest/values/AuthorCollection.php��yj\�Gr��+phar-io-manifest/values/ApplicationName.phpc�yj\c���8phar-io-manifest/exceptions/ManifestElementException.phpu�yj\u��w/�7phar-io-manifest/exceptions/ManifestLoaderException.phpm�yj\m8L:��5phar-io-manifest/exceptions/InvalidEmailException.php��yj\��3D�)phar-io-manifest/exceptions/Exception.phpn�yj\n��Y��?phar-io-manifest/exceptions/ManifestDocumentMapperException.php|�yj\|�A ��?phar-io-manifest/exceptions/InvalidApplicationNameException.php��yj\�6KQ�3phar-io-manifest/exceptions/InvalidUrlException.php��yj\�)���9phar-io-manifest/exceptions/ManifestDocumentException.phpv�yj\v��phar-io-manifest/LICENSEQ�yj\Q$W0�php-file-iterator/Iterator.php �yj\ �%�޶php-file-iterator/Facade.php� �yj\� k*�̶php-file-iterator/Factory.php��yj\��l Ҷphp-file-iterator/LICENSE�yj\J�U(�php-token-stream/Token.php-a�yj\-at̯�0php-token-stream/Token/Stream/CachingFactory.php��yj\� \h��!php-token-stream/Token/Stream.php�>�yj\�>����php-token-stream/LICENSE�yj\ +}� phpunit.xsdJ@�yj\J@��{�webmozart-assert/Assert.php���yj\��ހ�webmozart-assert/LICENSE<�yj\<t�}��!sebastian-environment/Console.php��yj\�,���!sebastian-environment/Runtime.php&�yj\&j�4�)sebastian-environment/OperatingSystem.php��yj\��`���sebastian-environment/LICENSE�yj\I���)sebastian-object-enumerator/Exception.php6�yj\6n$*a�*sebastian-object-enumerator/Enumerator.phpr�yj\rz�\��8sebastian-object-enumerator/InvalidArgumentException.phpx�yj\x��'��sebastian-version/Version.php��yj\�N\Ƕsebastian-version/LICENSE�yj\n�3sebastian-diff/Exception/ConfigurationException.phpC�yj\C<� 1�&sebastian-diff/Exception/Exception.php@�yj\@g�ն5sebastian-diff/Exception/InvalidArgumentException.php��yj\�g����Dsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.phpV�yj\VfնBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php �yj\ v�<�sebastian-diff/Line.phpO�yj\O'�� �sebastian-diff/Differ.php9%�yj\9%��D�4sebastian-diff/Output/DiffOutputBuilderInterface.php +�yj\ +�zk�8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php�(�yj\�(���N�4sebastian-diff/Output/AbstractChunkOutputBuilder.phpO�yj\OE�l�2sebastian-diff/Output/UnifiedDiffOutputBuilder.php* �yj\* ����/sebastian-diff/Output/DiffOnlyOutputBuilder.php��yj\��28>�sebastian-diff/Diff.php��yj\�3߬�sebastian-diff/Parser.php� �yj\� G�2�sebastian-diff/Chunk.phpm�yj\m�A��5sebastian-diff/LongestCommonSubsequenceCalculator.php<�yj\<����sebastian-diff/LICENSE �yj\ Dte#�@php-code-coverage/Exception/MissingCoversAnnotationException.php��yj\��u���0php-code-coverage/Exception/RuntimeException.phpo�yj\oH���)php-code-coverage/Exception/Exception.php}�yj\}=�0:�?php-code-coverage/Exception/CoveredCodeNotExecutedException.php��yj\��߁�8php-code-coverage/Exception/InvalidArgumentException.php�yj\ �ؕ�Cphp-code-coverage/Exception/UnintentionallyCoveredCodeException.php4�yj\43�ٶ#php-code-coverage/Report/Clover.php�(�yj\�(�+.� php-code-coverage/Report/PHP.phpJ�yj\JM� ��&php-code-coverage/Report/Xml/Tests.phpl�yj\l�^�W�*php-code-coverage/Report/Xml/Directory.phpW�yj\WS��˶'php-code-coverage/Report/Xml/Source.php��yj\���G�'php-code-coverage/Report/Xml/Totals.php��yj\�m�*��)php-code-coverage/Report/Xml/Coverage.php��yj\�tw�}�'php-code-coverage/Report/Xml/Report.php� �yj\� �� ��'php-code-coverage/Report/Xml/Method.php��yj\���"n�'php-code-coverage/Report/Xml/Facade.phpd �yj\d c��k�%php-code-coverage/Report/Xml/Unit.php} +�yj\} +��s��1php-code-coverage/Report/Xml/BuildInformation.php��yj\�Mn%�%php-code-coverage/Report/Xml/File.php��yj\��P��%php-code-coverage/Report/Xml/Node.phpM�yj\M�W��(php-code-coverage/Report/Xml/Project.php$ �yj\$ @/�_�4php-code-coverage/Report/Html/Renderer/Directory.php� �yj\� !2<�4php-code-coverage/Report/Html/Renderer/Dashboard.phpW%�yj\W%���)�Hphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svg��yj\���Z��Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0�yj\0�QUU�@php-code-coverage/Report/Html/Renderer/Template/js/popper.min.jsqO�yj\qO�v/Ͷ:php-code-coverage/Report/Html/Renderer/Template/js/file.js��yj\��'��?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.js�R�yj\�Rphp-code-coverage/Report/Html/Renderer/Template/file.html.dist +�yj\ +�ɳ�Fphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'�yj\'�O}�Cphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distG�yj\G�K〶Cphp-code-coverage/Report/Html/Renderer/Template/directory.html.dist��yj\�hG���Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.dist��yj\���s:�@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssX�yj\X'#��>php-code-coverage/Report/Html/Renderer/Template/css/custom.css�yj\�Ephp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.css�&�yj\�&1�L�=php-code-coverage/Report/Html/Renderer/Template/css/style.css��yj\���v�Aphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%�yj\X%�0,�/php-code-coverage/Report/Html/Renderer/File.php�I�yj\�IF�fж(php-code-coverage/Report/Html/Facade.php6�yj\6t��?�*php-code-coverage/Report/Html/Renderer.php� �yj\� {,(�!php-code-coverage/Report/Text.phpR"�yj\R"���#php-code-coverage/Report/Crap4j.php�yj\�q$�"php-code-coverage/CodeCoverage.phpar�yj\ar|����php-code-coverage/Version.php��yj\��� +$�#php-code-coverage/Driver/PHPDBG.phpu +�yj\u +� ��#php-code-coverage/Driver/Xdebug.php� +�yj\� +p:���#php-code-coverage/Driver/Driver.php��yj\�.�߸�php-code-coverage/Util.phpM�yj\MXq逶#php-code-coverage/Node/Iterator.php!�yj\!0n�ܶ$php-code-coverage/Node/Directory.phpX$�yj\X${��$�"php-code-coverage/Node/Builder.phpt�yj\t�o�/�php-code-coverage/Node/File.php@�yj\@ `�o�'php-code-coverage/Node/AbstractNode.php5�yj\5=K�ʶphp-code-coverage/Filter.phpl�yj\l� +`��php-code-coverage/LICENSE�yj\yM�F�sebastian-exporter/LICENSE�yj\��`�sebastian-exporter/Exporter.php�#�yj\�#!�Y��!phar-io-version/VersionNumber.php"�yj\"�v�޶phar-io-version/Version.php^�yj\^[A+�*phar-io-version/VersionConstraintValue.php +�yj\ +���>phar-io-version/constraints/SpecificMajorVersionConstraint.phpu�yj\ur��8phar-io-version/constraints/OrVersionConstraintGroup.php(�yj\(����Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php[�yj\[@��n�9phar-io-version/constraints/AndVersionConstraintGroup.php*�yj\* C�h�9phar-io-version/constraints/AbstractVersionConstraint.php��yj\�Whg��6phar-io-version/constraints/ExactVersionConstraint.phpZ�yj\Z ���1phar-io-version/constraints/VersionConstraint.php6�yj\6w�U��Ephar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php �yj\ � �4phar-io-version/constraints/AnyVersionConstraint.php��yj\����+phar-io-version/VersionConstraintParser.php� �yj\� ^�.�?phar-io-version/exceptions/InvalidPreReleaseSuffixException.phpv�yj\v����(phar-io-version/exceptions/Exception.phpl�yj\l�؈�6phar-io-version/exceptions/InvalidVersionException.php{�yj\{O��Dphar-io-version/exceptions/UnsupportedVersionConstraintException.php��yj\��`r�phar-io-version/LICENSE1�yj\1>��:�$phar-io-version/PreReleaseSuffix.phpg�yj\g��d��4sebastian-resource-operations/ResourceOperations.phpi�yj\i6G�%sebastian-resource-operations/LICENSE�yj\��r� + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\Types\Context; + +class FqsenResolver +{ + /** @var string Definition of the NAMESPACE operator in PHP */ + const OPERATOR_NAMESPACE = '\\'; + + public function resolve($fqsen, Context $context = null) + { + if ($context === null) { + $context = new Context(''); + } + + if ($this->isFqsen($fqsen)) { + return new Fqsen($fqsen); + } + + return $this->resolvePartialStructuralElementName($fqsen, $context); + } + + /** + * Tests whether the given type is a Fully Qualified Structural Element Name. + * + * @param string $type + * + * @return bool + */ + private function isFqsen($type) + { + return strpos($type, self::OPERATOR_NAMESPACE) === 0; + } + + /** + * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation + * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. + * + * @param string $type + * @param Context $context + * + * @return Fqsen + * @throws \InvalidArgumentException when type is not a valid FQSEN. + */ + private function resolvePartialStructuralElementName($type, Context $context) + { + $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); + + $namespaceAliases = $context->getNamespaceAliases(); + + // if the first segment is not an alias; prepend namespace name and return + if (!isset($namespaceAliases[$typeParts[0]])) { + $namespace = $context->getNamespace(); + if ('' !== $namespace) { + $namespace .= self::OPERATOR_NAMESPACE; + } + + return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); + } + + $typeParts[0] = $namespaceAliases[$typeParts[0]]; + + return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts)); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +interface Type +{ + public function __toString(); +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the 'resource' Type. + */ +final class Resource_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'resource'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +/** + * Provides information about the Context in which the DocBlock occurs that receives this context. + * + * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable + * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since + * you can provide a short form or make use of namespace aliases. + * + * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your + * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in + * which an associated class resides for its namespace and imports. + * + * @see ContextFactory::createFromClassReflector() + * @see ContextFactory::createForNamespace() + */ +final class Context +{ + /** @var string The current namespace. */ + private $namespace; + + /** @var array List of namespace aliases => Fully Qualified Namespace. */ + private $namespaceAliases; + + /** + * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) + * format (without a preceding `\`). + * + * @param string $namespace The namespace where this DocBlock resides in. + * @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace. + */ + public function __construct($namespace, array $namespaceAliases = []) + { + $this->namespace = ('global' !== $namespace && 'default' !== $namespace) + ? trim((string)$namespace, '\\') + : ''; + + foreach ($namespaceAliases as $alias => $fqnn) { + if ($fqnn[0] === '\\') { + $fqnn = substr($fqnn, 1); + } + if ($fqnn[strlen($fqnn) - 1] === '\\') { + $fqnn = substr($fqnn, 0, -1); + } + + $namespaceAliases[$alias] = $fqnn; + } + + $this->namespaceAliases = $namespaceAliases; + } + + /** + * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. + * + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent + * the alias for the imported Namespace. + * + * @return string[] + */ + public function getNamespaceAliases() + { + return $this->namespaceAliases; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. + */ +final class Scalar implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'scalar'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing a Boolean type. + */ +final class Boolean implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'bool'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the type 'string'. + */ +final class String_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'string'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the 'parent' type. + * + * Parent, as a Type, represents the parent class of class in which the associated element was defined. + */ +final class Parent_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'parent'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the 'self' type. + * + * Self, as a Type, represents the class in which the associated element was defined. + */ +final class Self_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'self'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing a nullable type. The real type is wrapped. + */ +final class Nullable implements Type +{ + /** + * @var Type + */ + private $realType; + + /** + * Initialises this nullable type using the real type embedded + * + * @param Type $realType + */ + public function __construct(Type $realType) + { + $this->realType = $realType; + } + + /** + * Provide access to the actual type directly, if needed. + * + * @return Type + */ + public function getActualType() + { + return $this->realType; + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return '?' . $this->realType->__toString(); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use ArrayIterator; +use IteratorAggregate; +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing a Compound Type. + * + * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated + * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type + * may contain a value with any of the given types. + */ +final class Compound implements Type, IteratorAggregate +{ + /** @var Type[] */ + private $types; + + /** + * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface. + * + * @param Type[] $types + * @throws \InvalidArgumentException when types are not all instance of Type + */ + public function __construct(array $types) + { + foreach ($types as $type) { + if (!$type instanceof Type) { + throw new \InvalidArgumentException('A compound type can only have other types as elements'); + } + } + + $this->types = $types; + } + + /** + * Returns the type at the given index. + * + * @param integer $index + * + * @return Type|null + */ + public function get($index) + { + if (!$this->has($index)) { + return null; + } + + return $this->types[$index]; + } + + /** + * Tests if this compound type has a type with the given index. + * + * @param integer $index + * + * @return bool + */ + public function has($index) + { + return isset($this->types[$index]); + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return implode('|', $this->types); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new ArrayIterator($this->types); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing iterable type + */ +final class Iterable_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'iterable'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the pseudo-type 'void'. + * + * Void is generally only used when working with return types as it signifies that the method intentionally does not + * return any value. + */ +final class Void_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'void'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing a Callable type. + */ +final class Callable_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'callable'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing an unknown, or mixed, type. + */ +final class Mixed_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'mixed'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Fqsen; +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing an object. + * + * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN, + * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects + * in general. + */ +final class Object_ implements Type +{ + /** @var Fqsen|null */ + private $fqsen; + + /** + * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'. + * + * @param Fqsen $fqsen + * @throws \InvalidArgumentException when provided $fqsen is not a valid type. + */ + public function __construct(Fqsen $fqsen = null) + { + if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) { + throw new \InvalidArgumentException( + 'Object types can only refer to a class, interface or trait but a method, function, constant or ' + . 'property was received: ' . (string)$fqsen + ); + } + + $this->fqsen = $fqsen; + } + + /** + * Returns the FQSEN associated with this object. + * + * @return Fqsen|null + */ + public function getFqsen() + { + return $this->fqsen; + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + if ($this->fqsen) { + return (string)$this->fqsen; + } + + return 'object'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing a Float. + */ +final class Float_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'float'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +final class Integer implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'int'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the '$this' pseudo-type. + * + * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is + * commonly used when documenting fluent interfaces since it represents that the same object is returned. + */ +final class This implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return '$this'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Represents an array type as described in the PSR-5, the PHPDoc Standard. + * + * An array can be represented in two forms: + * + * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'. + * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a + * type name. + */ +final class Array_ implements Type +{ + /** @var Type */ + private $valueType; + + /** @var Type */ + private $keyType; + + /** + * Initializes this representation of an array with the given Type or Fqsen. + * + * @param Type $valueType + * @param Type $keyType + */ + public function __construct(Type $valueType = null, Type $keyType = null) + { + if ($keyType === null) { + $keyType = new Compound([ new String_(), new Integer() ]); + } + if ($valueType === null) { + $valueType = new Mixed_(); + } + + $this->valueType = $valueType; + $this->keyType = $keyType; + } + + /** + * Returns the type for the keys of this array. + * + * @return Type + */ + public function getKeyType() + { + return $this->keyType; + } + + /** + * Returns the value for the keys of this array. + * + * @return Type + */ + public function getValueType() + { + return $this->valueType; + } + + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + if ($this->valueType instanceof Mixed_) { + return 'array'; + } + + return $this->valueType . '[]'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing the 'static' type. + * + * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does + * not take inheritance into account but static means that the return type is always that of the class of the called + * element. + * + * See the documentation on late static binding in the PHP Documentation for more information on the difference between + * static and self. + */ +final class Static_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'static'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +/** + * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor. + * + * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to + * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to + * Fully Qualified names. + * + * @see Context for more information. + */ +final class ContextFactory +{ + /** The literal used at the end of a use statement. */ + const T_LITERAL_END_OF_USE = ';'; + + /** The literal used between sets of use statements */ + const T_LITERAL_USE_SEPARATOR = ','; + + /** + * Build a Context given a Class Reflection. + * + * @param \Reflector $reflector + * + * @see Context for more information on Contexts. + * + * @return Context + */ + public function createFromReflector(\Reflector $reflector) + { + if (method_exists($reflector, 'getDeclaringClass')) { + $reflector = $reflector->getDeclaringClass(); + } + + $fileName = $reflector->getFileName(); + $namespace = $reflector->getNamespaceName(); + + if (file_exists($fileName)) { + return $this->createForNamespace($namespace, file_get_contents($fileName)); + } + + return new Context($namespace, []); + } + + /** + * Build a Context for a namespace in the provided file contents. + * + * @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes. + * @param string $fileContents the file's contents to retrieve the aliases from with the given namespace. + * + * @see Context for more information on Contexts. + * + * @return Context + */ + public function createForNamespace($namespace, $fileContents) + { + $namespace = trim($namespace, '\\'); + $useStatements = []; + $currentNamespace = ''; + $tokens = new \ArrayIterator(token_get_all($fileContents)); + + while ($tokens->valid()) { + switch ($tokens->current()[0]) { + case T_NAMESPACE: + $currentNamespace = $this->parseNamespace($tokens); + break; + case T_CLASS: + // Fast-forward the iterator through the class so that any + // T_USE tokens found within are skipped - these are not + // valid namespace use statements so should be ignored. + $braceLevel = 0; + $firstBraceFound = false; + while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { + if ($tokens->current() === '{' + || $tokens->current()[0] === T_CURLY_OPEN + || $tokens->current()[0] === T_DOLLAR_OPEN_CURLY_BRACES) { + if (!$firstBraceFound) { + $firstBraceFound = true; + } + $braceLevel++; + } + + if ($tokens->current() === '}') { + $braceLevel--; + } + $tokens->next(); + } + break; + case T_USE: + if ($currentNamespace === $namespace) { + $useStatements = array_merge($useStatements, $this->parseUseStatement($tokens)); + } + break; + } + $tokens->next(); + } + + return new Context($namespace, $useStatements); + } + + /** + * Deduce the name from tokens when we are at the T_NAMESPACE token. + * + * @param \ArrayIterator $tokens + * + * @return string + */ + private function parseNamespace(\ArrayIterator $tokens) + { + // skip to the first string or namespace separator + $this->skipToNextStringOrNamespaceSeparator($tokens); + + $name = ''; + while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) + ) { + $name .= $tokens->current()[1]; + $tokens->next(); + } + + return $name; + } + + /** + * Deduce the names of all imports when we are at the T_USE token. + * + * @param \ArrayIterator $tokens + * + * @return string[] + */ + private function parseUseStatement(\ArrayIterator $tokens) + { + $uses = []; + $continue = true; + + while ($continue) { + $this->skipToNextStringOrNamespaceSeparator($tokens); + + list($alias, $fqnn) = $this->extractUseStatement($tokens); + $uses[$alias] = $fqnn; + if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) { + $continue = false; + } + } + + return $uses; + } + + /** + * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. + * + * @param \ArrayIterator $tokens + * + * @return void + */ + private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens) + { + while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) { + $tokens->next(); + } + } + + /** + * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of + * a USE statement yet. + * + * @param \ArrayIterator $tokens + * + * @return string + */ + private function extractUseStatement(\ArrayIterator $tokens) + { + $result = ['']; + while ($tokens->valid() + && ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR) + && ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE) + ) { + if ($tokens->current()[0] === T_AS) { + $result[] = ''; + } + if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) { + $result[count($result) - 1] .= $tokens->current()[1]; + } + $tokens->next(); + } + + if (count($result) == 1) { + $backslashPos = strrpos($result[0], '\\'); + + if (false !== $backslashPos) { + $result[] = substr($result[0], $backslashPos + 1); + } else { + $result[] = $result[0]; + } + } + + return array_reverse($result); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\Types; + +use phpDocumentor\Reflection\Type; + +/** + * Value Object representing a null value or type. + */ +final class Null_ implements Type +{ + /** + * Returns a rendered output of the Type as it would be used in a DocBlock. + * + * @return string + */ + public function __toString() + { + return 'null'; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\Types\Array_; +use phpDocumentor\Reflection\Types\Compound; +use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\Iterable_; +use phpDocumentor\Reflection\Types\Nullable; +use phpDocumentor\Reflection\Types\Object_; + +final class TypeResolver +{ + /** @var string Definition of the ARRAY operator for types */ + const OPERATOR_ARRAY = '[]'; + + /** @var string Definition of the NAMESPACE operator in PHP */ + const OPERATOR_NAMESPACE = '\\'; + + /** @var string[] List of recognized keywords and unto which Value Object they map */ + private $keywords = array( + 'string' => Types\String_::class, + 'int' => Types\Integer::class, + 'integer' => Types\Integer::class, + 'bool' => Types\Boolean::class, + 'boolean' => Types\Boolean::class, + 'float' => Types\Float_::class, + 'double' => Types\Float_::class, + 'object' => Object_::class, + 'mixed' => Types\Mixed_::class, + 'array' => Array_::class, + 'resource' => Types\Resource_::class, + 'void' => Types\Void_::class, + 'null' => Types\Null_::class, + 'scalar' => Types\Scalar::class, + 'callback' => Types\Callable_::class, + 'callable' => Types\Callable_::class, + 'false' => Types\Boolean::class, + 'true' => Types\Boolean::class, + 'self' => Types\Self_::class, + '$this' => Types\This::class, + 'static' => Types\Static_::class, + 'parent' => Types\Parent_::class, + 'iterable' => Iterable_::class, + ); + + /** @var FqsenResolver */ + private $fqsenResolver; + + /** + * Initializes this TypeResolver with the means to create and resolve Fqsen objects. + * + * @param FqsenResolver $fqsenResolver + */ + public function __construct(FqsenResolver $fqsenResolver = null) + { + $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); + } + + /** + * Analyzes the given type and returns the FQCN variant. + * + * When a type is provided this method checks whether it is not a keyword or + * Fully Qualified Class Name. If so it will use the given namespace and + * aliases to expand the type to a FQCN representation. + * + * This method only works as expected if the namespace and aliases are set; + * no dynamic reflection is being performed here. + * + * @param string $type The relative or absolute type. + * @param Context $context + * + * @uses Context::getNamespace() to determine with what to prefix the type name. + * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be + * replaced with another namespace. + * + * @return Type|null + */ + public function resolve($type, Context $context = null) + { + if (!is_string($type)) { + throw new \InvalidArgumentException( + 'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true) + ); + } + + $type = trim($type); + if (!$type) { + throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); + } + + if ($context === null) { + $context = new Context(''); + } + + switch (true) { + case $this->isNullableType($type): + return $this->resolveNullableType($type, $context); + case $this->isKeyword($type): + return $this->resolveKeyword($type); + case ($this->isCompoundType($type)): + return $this->resolveCompoundType($type, $context); + case $this->isTypedArray($type): + return $this->resolveTypedArray($type, $context); + case $this->isFqsen($type): + return $this->resolveTypedObject($type); + case $this->isPartialStructuralElementName($type): + return $this->resolveTypedObject($type, $context); + // @codeCoverageIgnoreStart + default: + // I haven't got the foggiest how the logic would come here but added this as a defense. + throw new \RuntimeException( + 'Unable to resolve type "' . $type . '", there is no known method to resolve it' + ); + } + // @codeCoverageIgnoreEnd + } + + /** + * Adds a keyword to the list of Keywords and associates it with a specific Value Object. + * + * @param string $keyword + * @param string $typeClassName + * + * @return void + */ + public function addKeyword($keyword, $typeClassName) + { + if (!class_exists($typeClassName)) { + throw new \InvalidArgumentException( + 'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' + . ' but we could not find the class ' . $typeClassName + ); + } + + if (!in_array(Type::class, class_implements($typeClassName))) { + throw new \InvalidArgumentException( + 'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"' + ); + } + + $this->keywords[$keyword] = $typeClassName; + } + + /** + * Detects whether the given type represents an array. + * + * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. + * + * @return bool + */ + private function isTypedArray($type) + { + return substr($type, -2) === self::OPERATOR_ARRAY; + } + + /** + * Detects whether the given type represents a PHPDoc keyword. + * + * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. + * + * @return bool + */ + private function isKeyword($type) + { + return in_array(strtolower($type), array_keys($this->keywords), true); + } + + /** + * Detects whether the given type represents a relative structural element name. + * + * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. + * + * @return bool + */ + private function isPartialStructuralElementName($type) + { + return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type); + } + + /** + * Tests whether the given type is a Fully Qualified Structural Element Name. + * + * @param string $type + * + * @return bool + */ + private function isFqsen($type) + { + return strpos($type, self::OPERATOR_NAMESPACE) === 0; + } + + /** + * Tests whether the given type is a compound type (i.e. `string|int`). + * + * @param string $type + * + * @return bool + */ + private function isCompoundType($type) + { + return strpos($type, '|') !== false; + } + + /** + * Test whether the given type is a nullable type (i.e. `?string`) + * + * @param string $type + * + * @return bool + */ + private function isNullableType($type) + { + return $type[0] === '?'; + } + + /** + * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set. + * + * @param string $type + * @param Context $context + * + * @return Array_ + */ + private function resolveTypedArray($type, Context $context) + { + return new Array_($this->resolve(substr($type, 0, -2), $context)); + } + + /** + * Resolves the given keyword (such as `string`) into a Type object representing that keyword. + * + * @param string $type + * + * @return Type + */ + private function resolveKeyword($type) + { + $className = $this->keywords[strtolower($type)]; + + return new $className(); + } + + /** + * Resolves the given FQSEN string into an FQSEN object. + * + * @param string $type + * @param Context|null $context + * + * @return Object_ + */ + private function resolveTypedObject($type, Context $context = null) + { + return new Object_($this->fqsenResolver->resolve($type, $context)); + } + + /** + * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN. + * + * @param string $type + * @param Context $context + * + * @return Compound + */ + private function resolveCompoundType($type, Context $context) + { + $types = []; + + foreach (explode('|', $type) as $part) { + $types[] = $this->resolve($part, $context); + } + + return new Compound($types); + } + + /** + * Resolve nullable types (i.e. `?string`) into a Nullable type wrapper + * + * @param string $type + * @param Context $context + * + * @return Nullable + */ + private function resolveNullableType($type, Context $context) + { + return new Nullable($this->resolve(ltrim($type, '?'), $context)); + } +} +The MIT License (MIT) + +Copyright (c) 2010 Mike van Riel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +Object Reflector + +Copyright (c) 2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\ObjectProphecy; + +class ObjectProphecyException extends \RuntimeException implements ProphecyException +{ + private $objectProphecy; + + public function __construct($message, ObjectProphecy $objectProphecy) + { + parent::__construct($message); + + $this->objectProphecy = $objectProphecy; + } + + /** + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Exception\Exception; + +interface ProphecyException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\MethodProphecy; + +class MethodProphecyException extends ObjectProphecyException +{ + private $methodProphecy; + + public function __construct($message, MethodProphecy $methodProphecy) + { + parent::__construct($message, $methodProphecy->getObjectProphecy()); + + $this->methodProphecy = $methodProphecy; + } + + /** + * @return MethodProphecy + */ + public function getMethodProphecy() + { + return $this->methodProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class ClassNotFoundException extends DoubleException +{ + private $classname; + + /** + * @param string $message + * @param string $classname + */ + public function __construct($message, $classname) + { + parent::__construct($message); + + $this->classname = $classname; + } + + public function getClassname() + { + return $this->classname; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +class ClassCreatorException extends \RuntimeException implements DoublerException +{ + private $node; + + public function __construct($message, ClassNode $node) + { + parent::__construct($message); + + $this->node = $node; + } + + public function getClassNode() + { + return $this->node; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class InterfaceNotFoundException extends ClassNotFoundException +{ + public function getInterfaceName() + { + return $this->getClassname(); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class ReturnByReferenceException extends DoubleException +{ + private $classname; + private $methodName; + + /** + * @param string $message + * @param string $classname + * @param string $methodName + */ + public function __construct($message, $classname, $methodName) + { + parent::__construct($message); + + $this->classname = $classname; + $this->methodName = $methodName; + } + + public function getClassname() + { + return $this->classname; + } + + public function getMethodName() + { + return $this->methodName; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use RuntimeException; + +class DoubleException extends RuntimeException implements DoublerException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use ReflectionClass; + +class ClassMirrorException extends \RuntimeException implements DoublerException +{ + private $class; + + public function __construct($message, ReflectionClass $class) + { + parent::__construct($message); + + $this->class = $class; + } + + public function getReflectedClass() + { + return $this->class; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class MethodNotFoundException extends DoubleException +{ + /** + * @var string|object + */ + private $classname; + + /** + * @var string + */ + private $methodName; + + /** + * @var array + */ + private $arguments; + + /** + * @param string $message + * @param string|object $classname + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + */ + public function __construct($message, $classname, $methodName, $arguments = null) + { + parent::__construct($message); + + $this->classname = $classname; + $this->methodName = $methodName; + $this->arguments = $arguments; + } + + public function getClassname() + { + return $this->classname; + } + + public function getMethodName() + { + return $this->methodName; + } + + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use Prophecy\Exception\Exception; + +interface DoublerException extends Exception +{ +} +methodName = $methodName; + $this->className = $className; + } + + + /** + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + } + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception; + +/** + * Core Prophecy exception interface. + * All Prophecy exceptions implement it. + * + * @author Konstantin Kudryashov + */ +interface Exception +{ + /** + * @return string + */ + public function getMessage(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\ObjectProphecy; + +class AggregateException extends \RuntimeException implements PredictionException +{ + private $exceptions = array(); + private $objectProphecy; + + public function append(PredictionException $exception) + { + $message = $exception->getMessage(); + $message = strtr($message, array("\n" => "\n "))."\n"; + $message = empty($this->exceptions) ? $message : "\n" . $message; + + $this->message = rtrim($this->message.$message); + $this->exceptions[] = $exception; + } + + /** + * @return PredictionException[] + */ + public function getExceptions() + { + return $this->exceptions; + } + + public function setObjectProphecy(ObjectProphecy $objectProphecy) + { + $this->objectProphecy = $objectProphecy; + } + + /** + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; + +class UnexpectedCallsCountException extends UnexpectedCallsException +{ + private $expectedCount; + + public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) + { + parent::__construct($message, $methodProphecy, $calls); + + $this->expectedCount = intval($count); + } + + public function getExpectedCount() + { + return $this->expectedCount; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Exception; + +interface PredictionException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class UnexpectedCallsException extends MethodProphecyException implements PredictionException +{ + private $calls = array(); + + public function __construct($message, MethodProphecy $methodProphecy, array $calls) + { + parent::__construct($message, $methodProphecy); + + $this->calls = $calls; + } + + public function getCalls() + { + return $this->calls; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class NoCallsException extends MethodProphecyException implements PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use RuntimeException; + +/** + * Basic failed prediction exception. + * Use it for custom prediction failures. + * + * @author Konstantin Kudryashov + */ +class FailedPredictionException extends RuntimeException implements PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Call; + +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Prophecy\ObjectProphecy; + +class UnexpectedCallException extends ObjectProphecyException +{ + private $methodName; + private $arguments; + + public function __construct($message, ObjectProphecy $objectProphecy, + $methodName, array $arguments) + { + parent::__construct($message, $objectProphecy); + + $this->methodName = $methodName; + $this->arguments = $arguments; + } + + public function getMethodName() + { + return $this->methodName; + } + + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Call\Call; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Call\CallCenter; +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Exception\Prediction\AggregateException; +use Prophecy\Exception\Prediction\PredictionException; + +/** + * Object prophecy. + * + * @author Konstantin Kudryashov + */ +class ObjectProphecy implements ProphecyInterface +{ + private $lazyDouble; + private $callCenter; + private $revealer; + private $comparatorFactory; + + /** + * @var MethodProphecy[][] + */ + private $methodProphecies = array(); + + /** + * Initializes object prophecy. + * + * @param LazyDouble $lazyDouble + * @param CallCenter $callCenter + * @param RevealerInterface $revealer + * @param ComparatorFactory $comparatorFactory + */ + public function __construct( + LazyDouble $lazyDouble, + CallCenter $callCenter = null, + RevealerInterface $revealer = null, + ComparatorFactory $comparatorFactory = null + ) { + $this->lazyDouble = $lazyDouble; + $this->callCenter = $callCenter ?: new CallCenter; + $this->revealer = $revealer ?: new Revealer; + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Forces double to extend specific class. + * + * @param string $class + * + * @return $this + */ + public function willExtend($class) + { + $this->lazyDouble->setParentClass($class); + + return $this; + } + + /** + * Forces double to implement specific interface. + * + * @param string $interface + * + * @return $this + */ + public function willImplement($interface) + { + $this->lazyDouble->addInterface($interface); + + return $this; + } + + /** + * Sets constructor arguments. + * + * @param array $arguments + * + * @return $this + */ + public function willBeConstructedWith(array $arguments = null) + { + $this->lazyDouble->setArguments($arguments); + + return $this; + } + + /** + * Reveals double. + * + * @return object + * + * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface + */ + public function reveal() + { + $double = $this->lazyDouble->getInstance(); + + if (null === $double || !$double instanceof ProphecySubjectInterface) { + throw new ObjectProphecyException( + "Generated double must implement ProphecySubjectInterface, but it does not.\n". + 'It seems you have wrongly configured doubler without required ClassPatch.', + $this + ); + } + + $double->setProphecy($this); + + return $double; + } + + /** + * Adds method prophecy to object prophecy. + * + * @param MethodProphecy $methodProphecy + * + * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't + * have arguments wildcard + */ + public function addMethodProphecy(MethodProphecy $methodProphecy) + { + $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); + if (null === $argumentsWildcard) { + throw new MethodProphecyException(sprintf( + "Can not add prophecy for a method `%s::%s()`\n". + "as you did not specify arguments wildcard for it.", + get_class($this->reveal()), + $methodProphecy->getMethodName() + ), $methodProphecy); + } + + $methodName = $methodProphecy->getMethodName(); + + if (!isset($this->methodProphecies[$methodName])) { + $this->methodProphecies[$methodName] = array(); + } + + $this->methodProphecies[$methodName][] = $methodProphecy; + } + + /** + * Returns either all or related to single method prophecies. + * + * @param null|string $methodName + * + * @return MethodProphecy[] + */ + public function getMethodProphecies($methodName = null) + { + if (null === $methodName) { + return $this->methodProphecies; + } + + if (!isset($this->methodProphecies[$methodName])) { + return array(); + } + + return $this->methodProphecies[$methodName]; + } + + /** + * Makes specific method call. + * + * @param string $methodName + * @param array $arguments + * + * @return mixed + */ + public function makeProphecyMethodCall($methodName, array $arguments) + { + $arguments = $this->revealer->reveal($arguments); + $return = $this->callCenter->makeCall($this, $methodName, $arguments); + + return $this->revealer->reveal($return); + } + + /** + * Finds calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) + { + return $this->callCenter->findCalls($methodName, $wildcard); + } + + /** + * Checks that registered method predictions do not fail. + * + * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail + */ + public function checkProphecyMethodsPredictions() + { + $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); + $exception->setObjectProphecy($this); + + foreach ($this->methodProphecies as $prophecies) { + foreach ($prophecies as $prophecy) { + try { + $prophecy->checkPrediction(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + } + + if (count($exception->getExceptions())) { + throw $exception; + } + } + + /** + * Creates new method prophecy using specified method name and arguments. + * + * @param string $methodName + * @param array $arguments + * + * @return MethodProphecy + */ + public function __call($methodName, array $arguments) + { + $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); + + foreach ($this->getMethodProphecies($methodName) as $prophecy) { + $argumentsWildcard = $prophecy->getArgumentsWildcard(); + $comparator = $this->comparatorFactory->getComparatorFor( + $argumentsWildcard, $arguments + ); + + try { + $comparator->assertEquals($argumentsWildcard, $arguments); + return $prophecy; + } catch (ComparisonFailure $failure) {} + } + + return new MethodProphecy($this, $methodName, $arguments); + } + + /** + * Tries to get property value from double. + * + * @param string $name + * + * @return mixed + */ + public function __get($name) + { + return $this->reveal()->$name; + } + + /** + * Tries to set property value to double. + * + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + $this->reveal()->$name = $this->revealer->reveal($value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +use Prophecy\Argument; +use Prophecy\Prophet; +use Prophecy\Promise; +use Prophecy\Prediction; +use Prophecy\Exception\Doubler\MethodNotFoundException; +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Prophecy\MethodProphecyException; + +/** + * Method prophecy. + * + * @author Konstantin Kudryashov + */ +class MethodProphecy +{ + private $objectProphecy; + private $methodName; + private $argumentsWildcard; + private $promise; + private $prediction; + private $checkedPredictions = array(); + private $bound = false; + private $voidReturnType = false; + + /** + * Initializes method prophecy. + * + * @param ObjectProphecy $objectProphecy + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + * + * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found + */ + public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null) + { + $double = $objectProphecy->reveal(); + if (!method_exists($double, $methodName)) { + throw new MethodNotFoundException(sprintf( + 'Method `%s::%s()` is not defined.', get_class($double), $methodName + ), get_class($double), $methodName, $arguments); + } + + $this->objectProphecy = $objectProphecy; + $this->methodName = $methodName; + + $reflectedMethod = new \ReflectionMethod($double, $methodName); + if ($reflectedMethod->isFinal()) { + throw new MethodProphecyException(sprintf( + "Can not add prophecy for a method `%s::%s()`\n". + "as it is a final method.", + get_class($double), + $methodName + ), $this); + } + + if (null !== $arguments) { + $this->withArguments($arguments); + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) { + $type = (string) $reflectedMethod->getReturnType(); + + if ('void' === $type) { + $this->voidReturnType = true; + } + + $this->will(function () use ($type) { + switch ($type) { + case 'void': return; + case 'string': return ''; + case 'float': return 0.0; + case 'int': return 0; + case 'bool': return false; + case 'array': return array(); + + case 'callable': + case 'Closure': + return function () {}; + + case 'Traversable': + case 'Generator': + // Remove eval() when minimum version >=5.5 + /** @var callable $generator */ + $generator = eval('return function () { yield; };'); + return $generator(); + + default: + $prophet = new Prophet; + return $prophet->prophesize($type)->reveal(); + } + }); + } + } + + /** + * Sets argument wildcard. + * + * @param array|Argument\ArgumentsWildcard $arguments + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function withArguments($arguments) + { + if (is_array($arguments)) { + $arguments = new Argument\ArgumentsWildcard($arguments); + } + + if (!$arguments instanceof Argument\ArgumentsWildcard) { + throw new InvalidArgumentException(sprintf( + "Either an array or an instance of ArgumentsWildcard expected as\n". + 'a `MethodProphecy::withArguments()` argument, but got %s.', + gettype($arguments) + )); + } + + $this->argumentsWildcard = $arguments; + + return $this; + } + + /** + * Sets custom promise to the prophecy. + * + * @param callable|Promise\PromiseInterface $promise + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function will($promise) + { + if (is_callable($promise)) { + $promise = new Promise\CallbackPromise($promise); + } + + if (!$promise instanceof Promise\PromiseInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PromiseInterface, but got %s.', + gettype($promise) + )); + } + + $this->bindToObjectProphecy(); + $this->promise = $promise; + + return $this; + } + + /** + * Sets return promise to the prophecy. + * + * @see \Prophecy\Promise\ReturnPromise + * + * @return $this + */ + public function willReturn() + { + if ($this->voidReturnType) { + throw new MethodProphecyException( + "The method \"$this->methodName\" has a void return type, and so cannot return anything", + $this + ); + } + + return $this->will(new Promise\ReturnPromise(func_get_args())); + } + + /** + * Sets return argument promise to the prophecy. + * + * @param int $index The zero-indexed number of the argument to return + * + * @see \Prophecy\Promise\ReturnArgumentPromise + * + * @return $this + */ + public function willReturnArgument($index = 0) + { + if ($this->voidReturnType) { + throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this); + } + + return $this->will(new Promise\ReturnArgumentPromise($index)); + } + + /** + * Sets throw promise to the prophecy. + * + * @see \Prophecy\Promise\ThrowPromise + * + * @param string|\Exception $exception Exception class or instance + * + * @return $this + */ + public function willThrow($exception) + { + return $this->will(new Promise\ThrowPromise($exception)); + } + + /** + * Sets custom prediction to the prophecy. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function should($prediction) + { + if (is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PredictionInterface, but got %s.', + gettype($prediction) + )); + } + + $this->bindToObjectProphecy(); + $this->prediction = $prediction; + + return $this; + } + + /** + * Sets call prediction to the prophecy. + * + * @see \Prophecy\Prediction\CallPrediction + * + * @return $this + */ + public function shouldBeCalled() + { + return $this->should(new Prediction\CallPrediction); + } + + /** + * Sets no calls prediction to the prophecy. + * + * @see \Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotBeCalled() + { + return $this->should(new Prediction\NoCallsPrediction); + } + + /** + * Sets call times prediction to the prophecy. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @param $count + * + * @return $this + */ + public function shouldBeCalledTimes($count) + { + return $this->should(new Prediction\CallTimesPrediction($count)); + } + + /** + * Sets call times prediction to the prophecy. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @return $this + */ + public function shouldBeCalledOnce() + { + return $this->shouldBeCalledTimes(1); + } + + /** + * Checks provided prediction immediately. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function shouldHave($prediction) + { + if (is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PredictionInterface, but got %s.', + gettype($prediction) + )); + } + + if (null === $this->promise && !$this->voidReturnType) { + $this->willReturn(); + } + + $calls = $this->getObjectProphecy()->findProphecyMethodCalls( + $this->getMethodName(), + $this->getArgumentsWildcard() + ); + + try { + $prediction->check($calls, $this->getObjectProphecy(), $this); + $this->checkedPredictions[] = $prediction; + } catch (\Exception $e) { + $this->checkedPredictions[] = $prediction; + + throw $e; + } + + return $this; + } + + /** + * Checks call prediction. + * + * @see \Prophecy\Prediction\CallPrediction + * + * @return $this + */ + public function shouldHaveBeenCalled() + { + return $this->shouldHave(new Prediction\CallPrediction); + } + + /** + * Checks no calls prediction. + * + * @see \Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotHaveBeenCalled() + { + return $this->shouldHave(new Prediction\NoCallsPrediction); + } + + /** + * Checks no calls prediction. + * + * @see \Prophecy\Prediction\NoCallsPrediction + * @deprecated + * + * @return $this + */ + public function shouldNotBeenCalled() + { + return $this->shouldNotHaveBeenCalled(); + } + + /** + * Checks call times prediction. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @param int $count + * + * @return $this + */ + public function shouldHaveBeenCalledTimes($count) + { + return $this->shouldHave(new Prediction\CallTimesPrediction($count)); + } + + /** + * Checks call times prediction. + * + * @see \Prophecy\Prediction\CallTimesPrediction + * + * @return $this + */ + public function shouldHaveBeenCalledOnce() + { + return $this->shouldHaveBeenCalledTimes(1); + } + + /** + * Checks currently registered [with should(...)] prediction. + */ + public function checkPrediction() + { + if (null === $this->prediction) { + return; + } + + $this->shouldHave($this->prediction); + } + + /** + * Returns currently registered promise. + * + * @return null|Promise\PromiseInterface + */ + public function getPromise() + { + return $this->promise; + } + + /** + * Returns currently registered prediction. + * + * @return null|Prediction\PredictionInterface + */ + public function getPrediction() + { + return $this->prediction; + } + + /** + * Returns predictions that were checked on this object. + * + * @return Prediction\PredictionInterface[] + */ + public function getCheckedPredictions() + { + return $this->checkedPredictions; + } + + /** + * Returns object prophecy this method prophecy is tied to. + * + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } + + /** + * Returns method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * Returns arguments wildcard. + * + * @return Argument\ArgumentsWildcard + */ + public function getArgumentsWildcard() + { + return $this->argumentsWildcard; + } + + /** + * @return bool + */ + public function hasReturnVoid() + { + return $this->voidReturnType; + } + + private function bindToObjectProphecy() + { + if ($this->bound) { + return; + } + + $this->getObjectProphecy()->addMethodProphecy($this); + $this->bound = true; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Prophecies revealer interface. + * + * @author Konstantin Kudryashov + */ +interface RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Core Prophecy interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecyInterface +{ + /** + * Reveals prophecy object (double) . + * + * @return object + */ + public function reveal(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Basic prophecies revealer. + * + * @author Konstantin Kudryashov + */ +class Revealer implements RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value) + { + if (is_array($value)) { + return array_map(array($this, __FUNCTION__), $value); + } + + if (!is_object($value)) { + return $value; + } + + if ($value instanceof ProphecyInterface) { + $value = $value->reveal(); + } + + return $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Controllable doubles interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecySubjectInterface +{ + /** + * Sets subject prophecy. + * + * @param ProphecyInterface $prophecy + */ + public function setProphecy(ProphecyInterface $prophecy); + + /** + * Returns subject prophecy. + * + * @return ProphecyInterface + */ + public function getProphecy(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument; + +/** + * Arguments wildcarding. + * + * @author Konstantin Kudryashov + */ +class ArgumentsWildcard +{ + /** + * @var Token\TokenInterface[] + */ + private $tokens = array(); + private $string; + + /** + * Initializes wildcard. + * + * @param array $arguments Array of argument tokens or values + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + if (!$argument instanceof Token\TokenInterface) { + $argument = new Token\ExactValueToken($argument); + } + + $this->tokens[] = $argument; + } + } + + /** + * Calculates wildcard match score for provided arguments. + * + * @param array $arguments + * + * @return false|int False OR integer score (higher - better) + */ + public function scoreArguments(array $arguments) + { + if (0 == count($arguments) && 0 == count($this->tokens)) { + return 1; + } + + $arguments = array_values($arguments); + $totalScore = 0; + foreach ($this->tokens as $i => $token) { + $argument = isset($arguments[$i]) ? $arguments[$i] : null; + if (1 >= $score = $token->scoreArgument($argument)) { + return false; + } + + $totalScore += $score; + + if (true === $token->isLast()) { + return $totalScore; + } + } + + if (count($arguments) > count($this->tokens)) { + return false; + } + + return $totalScore; + } + + /** + * Returns string representation for wildcard. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = implode(', ', array_map(function ($token) { + return (string) $token; + }, $this->tokens)); + } + + return $this->string; + } + + /** + * @return array + */ + public function getTokens() + { + return $this->tokens; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Callback-verified token. + * + * @author Konstantin Kudryashov + */ +class CallbackToken implements TokenInterface +{ + private $callback; + + /** + * Initializes token. + * + * @param callable $callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackToken, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Scores 7 if callback returns true, false otherwise. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return call_user_func($this->callback, $argument) ? 7 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return 'callback()'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Logical AND token. + * + * @author Boris Mikhaylov + */ +class LogicalAndToken implements TokenInterface +{ + private $tokens = array(); + + /** + * @param array $arguments exact values or tokens + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + if (!$argument instanceof TokenInterface) { + $argument = new ExactValueToken($argument); + } + $this->tokens[] = $argument; + } + } + + /** + * Scores maximum score from scores returned by tokens for this argument if all of them score. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (0 === count($this->tokens)) { + return false; + } + + $maxScore = 0; + foreach ($this->tokens as $token) { + $score = $token->scoreArgument($argument); + if (false === $score) { + return false; + } + $maxScore = max($score, $maxScore); + } + + return $maxScore; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('bool(%s)', implode(' AND ', $this->tokens)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Logical NOT token. + * + * @author Boris Mikhaylov + */ +class LogicalNotToken implements TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $token; + + /** + * @param mixed $value exact value or token + */ + public function __construct($value) + { + $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value); + } + + /** + * Scores 4 when preset token does not match the argument. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return false === $this->token->scoreArgument($argument) ? 4 : false; + } + + /** + * Returns true if preset token is last. + * + * @return bool|int + */ + public function isLast() + { + return $this->token->isLast(); + } + + /** + * Returns originating token. + * + * @return TokenInterface + */ + public function getOriginatingToken() + { + return $this->token; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('not(%s)', $this->token); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Array every entry token. + * + * @author Adrien Brault + */ +class ArrayEveryEntryToken implements TokenInterface +{ + /** + * @var TokenInterface + */ + private $value; + + /** + * @param mixed $value exact value or token + */ + public function __construct($value) + { + if (!$value instanceof TokenInterface) { + $value = new ExactValueToken($value); + } + + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function scoreArgument($argument) + { + if (!$argument instanceof \Traversable && !is_array($argument)) { + return false; + } + + $scores = array(); + foreach ($argument as $key => $argumentEntry) { + $scores[] = $this->value->scoreArgument($argumentEntry); + } + + if (empty($scores) || in_array(false, $scores, true)) { + return false; + } + + return array_sum($scores) / count($scores); + } + + /** + * {@inheritdoc} + */ + public function isLast() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('[%s, ..., %s]', $this->value, $this->value); + } + + /** + * @return TokenInterface + */ + public function getValue() + { + return $this->value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Exact value token. + * + * @author Konstantin Kudryashov + */ +class ExactValueToken implements TokenInterface +{ + private $value; + private $string; + private $util; + private $comparatorFactory; + + /** + * Initializes token. + * + * @param mixed $value + * @param StringUtil $util + * @param ComparatorFactory $comparatorFactory + */ + public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) + { + $this->value = $value; + $this->util = $util ?: new StringUtil(); + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Scores 10 if argument matches preset value. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (is_object($argument) && is_object($this->value)) { + $comparator = $this->comparatorFactory->getComparatorFor( + $argument, $this->value + ); + + try { + $comparator->assertEquals($argument, $this->value); + return 10; + } catch (ComparisonFailure $failure) {} + } + + // If either one is an object it should be castable to a string + if (is_object($argument) xor is_object($this->value)) { + if (is_object($argument) && !method_exists($argument, '__toString')) { + return false; + } + + if (is_object($this->value) && !method_exists($this->value, '__toString')) { + return false; + } + } elseif (is_numeric($argument) && is_numeric($this->value)) { + // noop + } elseif (gettype($argument) !== gettype($this->value)) { + return false; + } + + return $argument == $this->value ? 10 : false; + } + + /** + * Returns preset value against which token checks arguments. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = sprintf('exact(%s)', $this->util->stringify($this->value)); + } + + return $this->string; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Value type token. + * + * @author Konstantin Kudryashov + */ +class TypeToken implements TokenInterface +{ + private $type; + + /** + * @param string $type + */ + public function __construct($type) + { + $checker = "is_{$type}"; + if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) { + throw new InvalidArgumentException(sprintf( + 'Type or class name expected as an argument to TypeToken, but got %s.', $type + )); + } + + $this->type = $type; + } + + /** + * Scores 5 if argument has the same type this token was constructed with. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + $checker = "is_{$this->type}"; + if (function_exists($checker)) { + return call_user_func($checker, $argument) ? 5 : false; + } + + return $argument instanceof $this->type ? 5 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('type(%s)', $this->type); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Approximate value token + * + * @author Daniel Leech + */ +class ApproximateValueToken implements TokenInterface +{ + private $value; + private $precision; + + public function __construct($value, $precision = 0) + { + $this->value = $value; + $this->precision = $precision; + } + + /** + * {@inheritdoc} + */ + public function scoreArgument($argument) + { + return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false; + } + + /** + * {@inheritdoc} + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('≅%s', round($this->value, $this->precision)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Any values token. + * + * @author Konstantin Kudryashov + */ +class AnyValuesToken implements TokenInterface +{ + /** + * Always scores 2 for any argument. + * + * @param $argument + * + * @return int + */ + public function scoreArgument($argument) + { + return 2; + } + + /** + * Returns true to stop wildcard from processing other tokens. + * + * @return bool + */ + public function isLast() + { + return true; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return '* [, ...]'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Util\StringUtil; + +/** + * Identical value token. + * + * @author Florian Voutzinos + */ +class IdenticalValueToken implements TokenInterface +{ + private $value; + private $string; + private $util; + + /** + * Initializes token. + * + * @param mixed $value + * @param StringUtil $util + */ + public function __construct($value, StringUtil $util = null) + { + $this->value = $value; + $this->util = $util ?: new StringUtil(); + } + + /** + * Scores 11 if argument matches preset value. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return $argument === $this->value ? 11 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = sprintf('identical(%s)', $this->util->stringify($this->value)); + } + + return $this->string; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Array elements count token. + * + * @author Boris Mikhaylov + */ + +class ArrayCountToken implements TokenInterface +{ + private $count; + + /** + * @param integer $value + */ + public function __construct($value) + { + $this->count = $value; + } + + /** + * Scores 6 when argument has preset number of elements. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('count(%s)', $this->count); + } + + /** + * Returns true if object is either array or instance of \Countable + * + * @param $argument + * @return bool + */ + private function isCountable($argument) + { + return (is_array($argument) || $argument instanceof \Countable); + } + + /** + * Returns true if $argument has expected number of elements + * + * @param array|\Countable $argument + * + * @return bool + */ + private function hasProperCount($argument) + { + return $this->count === count($argument); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Array entry token. + * + * @author Boris Mikhaylov + */ +class ArrayEntryToken implements TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $key; + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $value; + + /** + * @param mixed $key exact value or token + * @param mixed $value exact value or token + */ + public function __construct($key, $value) + { + $this->key = $this->wrapIntoExactValueToken($key); + $this->value = $this->wrapIntoExactValueToken($value); + } + + /** + * Scores half of combined scores from key and value tokens for same entry. Capped at 8. + * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. + * + * @param array|\ArrayAccess|\Traversable $argument + * + * @throws \Prophecy\Exception\InvalidArgumentException + * @return bool|int + */ + public function scoreArgument($argument) + { + if ($argument instanceof \Traversable) { + $argument = iterator_to_array($argument); + } + + if ($argument instanceof \ArrayAccess) { + $argument = $this->convertArrayAccessToEntry($argument); + } + + if (!is_array($argument) || empty($argument)) { + return false; + } + + $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument)); + $valueScores = array_map(array($this->value,'scoreArgument'), $argument); + $scoreEntry = function ($value, $key) { + return $value && $key ? min(8, ($key + $value) / 2) : false; + }; + + return max(array_map($scoreEntry, $valueScores, $keyScores)); + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('[..., %s => %s, ...]', $this->key, $this->value); + } + + /** + * Returns key + * + * @return TokenInterface + */ + public function getKey() + { + return $this->key; + } + + /** + * Returns value + * + * @return TokenInterface + */ + public function getValue() + { + return $this->value; + } + + /** + * Wraps non token $value into ExactValueToken + * + * @param $value + * @return TokenInterface + */ + private function wrapIntoExactValueToken($value) + { + return $value instanceof TokenInterface ? $value : new ExactValueToken($value); + } + + /** + * Converts instance of \ArrayAccess to key => value array entry + * + * @param \ArrayAccess $object + * + * @return array|null + * @throws \Prophecy\Exception\InvalidArgumentException + */ + private function convertArrayAccessToEntry(\ArrayAccess $object) + { + if (!$this->key instanceof ExactValueToken) { + throw new InvalidArgumentException(sprintf( + 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL. + 'But you used `%s`.', + $this->key + )); + } + + $key = $this->key->getValue(); + + return $object->offsetExists($key) ? array($key => $object[$key]) : array(); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Argument token interface. + * + * @author Konstantin Kudryashov + */ +interface TokenInterface +{ + /** + * Calculates token match score for provided argument. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument); + + /** + * Returns true if this token prevents check of other tokens (is last one). + * + * @return bool|int + */ + public function isLast(); + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * String contains token. + * + * @author Peter Mitchell + */ +class StringContainsToken implements TokenInterface +{ + private $value; + + /** + * Initializes token. + * + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + public function scoreArgument($argument) + { + return is_string($argument) && strpos($argument, $this->value) !== false ? 6 : false; + } + + /** + * Returns preset value against which token checks arguments. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('contains("%s")', $this->value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Any single value token. + * + * @author Konstantin Kudryashov + */ +class AnyValueToken implements TokenInterface +{ + /** + * Always scores 3 for any argument. + * + * @param $argument + * + * @return int + */ + public function scoreArgument($argument) + { + return 3; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return '*'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Object state-checker token. + * + * @author Konstantin Kudryashov + */ +class ObjectStateToken implements TokenInterface +{ + private $name; + private $value; + private $util; + private $comparatorFactory; + + /** + * Initializes token. + * + * @param string $methodName + * @param mixed $value Expected return value + * @param null|StringUtil $util + * @param ComparatorFactory $comparatorFactory + */ + public function __construct( + $methodName, + $value, + StringUtil $util = null, + ComparatorFactory $comparatorFactory = null + ) { + $this->name = $methodName; + $this->value = $value; + $this->util = $util ?: new StringUtil; + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Scores 8 if argument is an object, which method returns expected value. + * + * @param mixed $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (is_object($argument) && method_exists($argument, $this->name)) { + $actual = call_user_func(array($argument, $this->name)); + + $comparator = $this->comparatorFactory->getComparatorFor( + $this->value, $actual + ); + + try { + $comparator->assertEquals($this->value, $actual); + return 8; + } catch (ComparisonFailure $failure) { + return false; + } + } + + if (is_object($argument) && property_exists($argument, $this->name)) { + return $argument->{$this->name} === $this->value ? 8 : false; + } + + return false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('state(%s(), %s)', + $this->name, + $this->util->stringify($this->value) + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +/** + * Reflection interface. + * All reflected classes implement this interface. + * + * @author Konstantin Kudryashov + */ +interface ReflectionInterface +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +use Prophecy\Exception\Doubler\ClassCreatorException; + +/** + * Class creator. + * Creates specific class in current environment. + * + * @author Konstantin Kudryashov + */ +class ClassCreator +{ + private $generator; + + /** + * Initializes creator. + * + * @param ClassCodeGenerator $generator + */ + public function __construct(ClassCodeGenerator $generator = null) + { + $this->generator = $generator ?: new ClassCodeGenerator; + } + + /** + * Creates class. + * + * @param string $classname + * @param Node\ClassNode $class + * + * @return mixed + * + * @throws \Prophecy\Exception\Doubler\ClassCreatorException + */ + public function create($classname, Node\ClassNode $class) + { + $code = $this->generator->generate($classname, $class); + $return = eval($code); + + if (!class_exists($classname, false)) { + if (count($class->getInterfaces())) { + throw new ClassCreatorException(sprintf( + 'Could not double `%s` and implement interfaces: [%s].', + $class->getParentClass(), implode(', ', $class->getInterfaces()) + ), $class); + } + + throw new ClassCreatorException( + sprintf('Could not double `%s`.', $class->getParentClass()), + $class + ); + } + + return $return; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Doubler\Generator\TypeHintReference; +use Prophecy\Exception\InvalidArgumentException; + +/** + * Method node. + * + * @author Konstantin Kudryashov + */ +class MethodNode +{ + private $name; + private $code; + private $visibility = 'public'; + private $static = false; + private $returnsReference = false; + private $returnType; + private $nullableReturnType = false; + + /** + * @var ArgumentNode[] + */ + private $arguments = array(); + + /** + * @var TypeHintReference + */ + private $typeHintReference; + + /** + * @param string $name + * @param string $code + */ + public function __construct($name, $code = null, TypeHintReference $typeHintReference = null) + { + $this->name = $name; + $this->code = $code; + $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); + } + + public function getVisibility() + { + return $this->visibility; + } + + /** + * @param string $visibility + */ + public function setVisibility($visibility) + { + $visibility = strtolower($visibility); + + if (!in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(sprintf( + '`%s` method visibility is not supported.', $visibility + )); + } + + $this->visibility = $visibility; + } + + public function isStatic() + { + return $this->static; + } + + public function setStatic($static = true) + { + $this->static = (bool) $static; + } + + public function returnsReference() + { + return $this->returnsReference; + } + + public function setReturnsReference() + { + $this->returnsReference = true; + } + + public function getName() + { + return $this->name; + } + + public function addArgument(ArgumentNode $argument) + { + $this->arguments[] = $argument; + } + + /** + * @return ArgumentNode[] + */ + public function getArguments() + { + return $this->arguments; + } + + public function hasReturnType() + { + return null !== $this->returnType; + } + + /** + * @param string $type + */ + public function setReturnType($type = null) + { + if ($type === '' || $type === null) { + $this->returnType = null; + return; + } + $typeMap = array( + 'double' => 'float', + 'real' => 'float', + 'boolean' => 'bool', + 'integer' => 'int', + ); + if (isset($typeMap[$type])) { + $type = $typeMap[$type]; + } + $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? + $type : + '\\' . ltrim($type, '\\'); + } + + public function getReturnType() + { + return $this->returnType; + } + + /** + * @param bool $bool + */ + public function setNullableReturnType($bool = true) + { + $this->nullableReturnType = (bool) $bool; + } + + /** + * @return bool + */ + public function hasNullableReturnType() + { + return $this->nullableReturnType; + } + + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + public function getCode() + { + if ($this->returnsReference) + { + return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; + } + + return (string) $this->code; + } + + public function useParentCode() + { + $this->code = sprintf( + 'return parent::%s(%s);', $this->getName(), implode(', ', + array_map(array($this, 'generateArgument'), $this->arguments) + ) + ); + } + + private function generateArgument(ArgumentNode $arg) + { + $argument = '$'.$arg->getName(); + + if ($arg->isVariadic()) { + $argument = '...'.$argument; + } + + return $argument; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\Doubler\MethodNotExtendableException; +use Prophecy\Exception\InvalidArgumentException; + +/** + * Class node. + * + * @author Konstantin Kudryashov + */ +class ClassNode +{ + private $parentClass = 'stdClass'; + private $interfaces = array(); + private $properties = array(); + private $unextendableMethods = array(); + + /** + * @var MethodNode[] + */ + private $methods = array(); + + public function getParentClass() + { + return $this->parentClass; + } + + /** + * @param string $class + */ + public function setParentClass($class) + { + $this->parentClass = $class ?: 'stdClass'; + } + + /** + * @return string[] + */ + public function getInterfaces() + { + return $this->interfaces; + } + + /** + * @param string $interface + */ + public function addInterface($interface) + { + if ($this->hasInterface($interface)) { + return; + } + + array_unshift($this->interfaces, $interface); + } + + /** + * @param string $interface + * + * @return bool + */ + public function hasInterface($interface) + { + return in_array($interface, $this->interfaces); + } + + public function getProperties() + { + return $this->properties; + } + + public function addProperty($name, $visibility = 'public') + { + $visibility = strtolower($visibility); + + if (!in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(sprintf( + '`%s` property visibility is not supported.', $visibility + )); + } + + $this->properties[$name] = $visibility; + } + + /** + * @return MethodNode[] + */ + public function getMethods() + { + return $this->methods; + } + + public function addMethod(MethodNode $method, $force = false) + { + if (!$this->isExtendable($method->getName())){ + $message = sprintf( + 'Method `%s` is not extendable, so can not be added.', $method->getName() + ); + throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); + } + + if ($force || !isset($this->methods[$method->getName()])) { + $this->methods[$method->getName()] = $method; + } + } + + public function removeMethod($name) + { + unset($this->methods[$name]); + } + + /** + * @param string $name + * + * @return MethodNode|null + */ + public function getMethod($name) + { + return $this->hasMethod($name) ? $this->methods[$name] : null; + } + + /** + * @param string $name + * + * @return bool + */ + public function hasMethod($name) + { + return isset($this->methods[$name]); + } + + /** + * @return string[] + */ + public function getUnextendableMethods() + { + return $this->unextendableMethods; + } + + /** + * @param string $unextendableMethod + */ + public function addUnextendableMethod($unextendableMethod) + { + if (!$this->isExtendable($unextendableMethod)){ + return; + } + $this->unextendableMethods[] = $unextendableMethod; + } + + /** + * @param string $method + * @return bool + */ + public function isExtendable($method) + { + return !in_array($method, $this->unextendableMethods); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +/** + * Argument node. + * + * @author Konstantin Kudryashov + */ +class ArgumentNode +{ + private $name; + private $typeHint; + private $default; + private $optional = false; + private $byReference = false; + private $isVariadic = false; + private $isNullable = false; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getTypeHint() + { + return $this->typeHint; + } + + public function setTypeHint($typeHint = null) + { + $this->typeHint = $typeHint; + } + + public function hasDefault() + { + return $this->isOptional() && !$this->isVariadic(); + } + + public function getDefault() + { + return $this->default; + } + + public function setDefault($default = null) + { + $this->optional = true; + $this->default = $default; + } + + public function isOptional() + { + return $this->optional; + } + + public function setAsPassedByReference($byReference = true) + { + $this->byReference = $byReference; + } + + public function isPassedByReference() + { + return $this->byReference; + } + + public function setAsVariadic($isVariadic = true) + { + $this->isVariadic = $isVariadic; + } + + public function isVariadic() + { + return $this->isVariadic; + } + + public function isNullable() + { + return $this->isNullable; + } + + public function setAsNullable($isNullable = true) + { + $this->isNullable = $isNullable; + } +} += 50400; + + case 'bool': + case 'float': + case 'int': + case 'string': + return PHP_VERSION_ID >= 70000; + + case 'iterable': + return PHP_VERSION_ID >= 70100; + + case 'object': + return PHP_VERSION_ID >= 70200; + + default: + return false; + } + } + + public function isBuiltInReturnTypeHint($type) + { + if ($type === 'void') { + return PHP_VERSION_ID >= 70100; + } + + return $this->isBuiltInParamTypeHint($type); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +/** + * Class code creator. + * Generates PHP code for specific class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassCodeGenerator +{ + /** + * @var TypeHintReference + */ + private $typeHintReference; + + public function __construct(TypeHintReference $typeHintReference = null) + { + $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); + } + + /** + * Generates PHP code for class node. + * + * @param string $classname + * @param Node\ClassNode $class + * + * @return string + */ + public function generate($classname, Node\ClassNode $class) + { + $parts = explode('\\', $classname); + $classname = array_pop($parts); + $namespace = implode('\\', $parts); + + $code = sprintf("class %s extends \%s implements %s {\n", + $classname, $class->getParentClass(), implode(', ', + array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) + ) + ); + + foreach ($class->getProperties() as $name => $visibility) { + $code .= sprintf("%s \$%s;\n", $visibility, $name); + } + $code .= "\n"; + + foreach ($class->getMethods() as $method) { + $code .= $this->generateMethod($method)."\n"; + } + $code .= "\n}"; + + return sprintf("namespace %s {\n%s\n}", $namespace, $code); + } + + private function generateMethod(Node\MethodNode $method) + { + $php = sprintf("%s %s function %s%s(%s)%s {\n", + $method->getVisibility(), + $method->isStatic() ? 'static' : '', + $method->returnsReference() ? '&':'', + $method->getName(), + implode(', ', $this->generateArguments($method->getArguments())), + $this->getReturnType($method) + ); + $php .= $method->getCode()."\n"; + + return $php.'}'; + } + + /** + * @return string + */ + private function getReturnType(Node\MethodNode $method) + { + if (version_compare(PHP_VERSION, '7.1', '>=')) { + if ($method->hasReturnType()) { + return $method->hasNullableReturnType() + ? sprintf(': ?%s', $method->getReturnType()) + : sprintf(': %s', $method->getReturnType()); + } + } + + if (version_compare(PHP_VERSION, '7.0', '>=')) { + return $method->hasReturnType() && $method->getReturnType() !== 'void' + ? sprintf(': %s', $method->getReturnType()) + : ''; + } + + return ''; + } + + private function generateArguments(array $arguments) + { + $typeHintReference = $this->typeHintReference; + return array_map(function (Node\ArgumentNode $argument) use ($typeHintReference) { + $php = ''; + + if (version_compare(PHP_VERSION, '7.1', '>=')) { + $php .= $argument->isNullable() ? '?' : ''; + } + + if ($hint = $argument->getTypeHint()) { + $php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint; + } + + $php .= ' '.($argument->isPassedByReference() ? '&' : ''); + + $php .= $argument->isVariadic() ? '...' : ''; + + $php .= '$'.$argument->getName(); + + if ($argument->isOptional() && !$argument->isVariadic()) { + $php .= ' = '.var_export($argument->getDefault(), true); + } + + return $php; + }, $arguments); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Doubler\ClassMirrorException; +use ReflectionClass; +use ReflectionMethod; +use ReflectionParameter; + +/** + * Class mirror. + * Core doubler class. Mirrors specific class and/or interfaces into class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassMirror +{ + private static $reflectableMethods = array( + '__construct', + '__destruct', + '__sleep', + '__wakeup', + '__toString', + '__call', + '__invoke' + ); + + /** + * Reflects provided arguments into class node. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return Node\ClassNode + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function reflect(ReflectionClass $class = null, array $interfaces) + { + $node = new Node\ClassNode; + + if (null !== $class) { + if (true === $class->isInterface()) { + throw new InvalidArgumentException(sprintf( + "Could not reflect %s as a class, because it\n". + "is interface - use the second argument instead.", + $class->getName() + )); + } + + $this->reflectClassToNode($class, $node); + } + + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(sprintf( + "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". + "a second argument to `ClassMirror::reflect(...)`, but got %s.", + is_object($interface) ? get_class($interface).' class' : gettype($interface) + )); + } + if (false === $interface->isInterface()) { + throw new InvalidArgumentException(sprintf( + "Could not reflect %s as an interface, because it\n". + "is class - use the first argument instead.", + $interface->getName() + )); + } + + $this->reflectInterfaceToNode($interface, $node); + } + + $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface'); + + return $node; + } + + private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node) + { + if (true === $class->isFinal()) { + throw new ClassMirrorException(sprintf( + 'Could not reflect class %s as it is marked final.', $class->getName() + ), $class); + } + + $node->setParentClass($class->getName()); + + foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { + if (false === $method->isProtected()) { + continue; + } + + $this->reflectMethodToNode($method, $node); + } + + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if (0 === strpos($method->getName(), '_') + && !in_array($method->getName(), self::$reflectableMethods)) { + continue; + } + + if (true === $method->isFinal()) { + $node->addUnextendableMethod($method->getName()); + continue; + } + + $this->reflectMethodToNode($method, $node); + } + } + + private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node) + { + $node->addInterface($interface->getName()); + + foreach ($interface->getMethods() as $method) { + $this->reflectMethodToNode($method, $node); + } + } + + private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode) + { + $node = new Node\MethodNode($method->getName()); + + if (true === $method->isProtected()) { + $node->setVisibility('protected'); + } + + if (true === $method->isStatic()) { + $node->setStatic(); + } + + if (true === $method->returnsReference()) { + $node->setReturnsReference(); + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) { + $returnType = (string) $method->getReturnType(); + $returnTypeLower = strtolower($returnType); + + if ('self' === $returnTypeLower) { + $returnType = $method->getDeclaringClass()->getName(); + } + if ('parent' === $returnTypeLower) { + $returnType = $method->getDeclaringClass()->getParentClass()->getName(); + } + + $node->setReturnType($returnType); + + if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) { + $node->setNullableReturnType(true); + } + } + + if (is_array($params = $method->getParameters()) && count($params)) { + foreach ($params as $param) { + $this->reflectArgumentToNode($param, $node); + } + } + + $classNode->addMethod($node); + } + + private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode) + { + $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); + $node = new Node\ArgumentNode($name); + + $node->setTypeHint($this->getTypeHint($parameter)); + + if ($this->isVariadic($parameter)) { + $node->setAsVariadic(); + } + + if ($this->hasDefaultValue($parameter)) { + $node->setDefault($this->getDefaultValue($parameter)); + } + + if ($parameter->isPassedByReference()) { + $node->setAsPassedByReference(); + } + + $node->setAsNullable($this->isNullable($parameter)); + + $methodNode->addArgument($node); + } + + private function hasDefaultValue(ReflectionParameter $parameter) + { + if ($this->isVariadic($parameter)) { + return false; + } + + if ($parameter->isDefaultValueAvailable()) { + return true; + } + + return $parameter->isOptional() || $this->isNullable($parameter); + } + + private function getDefaultValue(ReflectionParameter $parameter) + { + if (!$parameter->isDefaultValueAvailable()) { + return null; + } + + return $parameter->getDefaultValue(); + } + + private function getTypeHint(ReflectionParameter $parameter) + { + if (null !== $className = $this->getParameterClassName($parameter)) { + return $className; + } + + if (true === $parameter->isArray()) { + return 'array'; + } + + if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) { + return 'callable'; + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) { + return (string) $parameter->getType(); + } + + return null; + } + + private function isVariadic(ReflectionParameter $parameter) + { + return PHP_VERSION_ID >= 50600 && $parameter->isVariadic(); + } + + private function isNullable(ReflectionParameter $parameter) + { + return $parameter->allowsNull() && null !== $this->getTypeHint($parameter); + } + + private function getParameterClassName(ReflectionParameter $parameter) + { + try { + return $parameter->getClass() ? $parameter->getClass()->getName() : null; + } catch (\ReflectionException $e) { + preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches); + + return isset($matches[1]) ? $matches[1] : null; + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Doubler\ClassPatch\ClassPatchInterface; +use Prophecy\Doubler\Generator\ClassMirror; +use Prophecy\Doubler\Generator\ClassCreator; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class Doubler +{ + private $mirror; + private $creator; + private $namer; + + /** + * @var ClassPatchInterface[] + */ + private $patches = array(); + + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * Initializes doubler. + * + * @param ClassMirror $mirror + * @param ClassCreator $creator + * @param NameGenerator $namer + */ + public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, + NameGenerator $namer = null) + { + $this->mirror = $mirror ?: new ClassMirror; + $this->creator = $creator ?: new ClassCreator; + $this->namer = $namer ?: new NameGenerator; + } + + /** + * Returns list of registered class patches. + * + * @return ClassPatchInterface[] + */ + public function getClassPatches() + { + return $this->patches; + } + + /** + * Registers new class patch. + * + * @param ClassPatchInterface $patch + */ + public function registerClassPatch(ClassPatchInterface $patch) + { + $this->patches[] = $patch; + + @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { + return $patch2->getPriority() - $patch1->getPriority(); + }); + } + + /** + * Creates double from specific class or/and list of interfaces. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces Array of ReflectionClass instances + * @param array $args Constructor arguments + * + * @return DoubleInterface + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function double(ReflectionClass $class = null, array $interfaces, array $args = null) + { + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(sprintf( + "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". + "a second argument to `Doubler::double(...)`, but got %s.", + is_object($interface) ? get_class($interface).' class' : gettype($interface) + )); + } + } + + $classname = $this->createDoubleClass($class, $interfaces); + $reflection = new ReflectionClass($classname); + + if (null !== $args) { + return $reflection->newInstanceArgs($args); + } + if ((null === $constructor = $reflection->getConstructor()) + || ($constructor->isPublic() && !$constructor->isFinal())) { + return $reflection->newInstance(); + } + + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + + return $this->instantiator->instantiate($classname); + } + + /** + * Creates double class and returns its FQN. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $name = $this->namer->name($class, $interfaces); + $node = $this->mirror->reflect($class, $interfaces); + + foreach ($this->patches as $patch) { + if ($patch->supports($node)) { + $patch->apply($node); + } + } + + $this->creator->create($name, $node); + + return $name; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class CachedDoubler extends Doubler +{ + private $classes = array(); + + /** + * {@inheritdoc} + */ + public function registerClassPatch(ClassPatch\ClassPatchInterface $patch) + { + $this->classes[] = array(); + + parent::registerClassPatch($patch); + } + + /** + * {@inheritdoc} + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $classId = $this->generateClassId($class, $interfaces); + if (isset($this->classes[$classId])) { + return $this->classes[$classId]; + } + + return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces); + } + + /** + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + private function generateClassId(ReflectionClass $class = null, array $interfaces) + { + $parts = array(); + if (null !== $class) { + $parts[] = $class->getName(); + } + foreach ($interfaces as $interface) { + $parts[] = $interface->getName(); + } + sort($parts); + + return md5(implode('', $parts)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use ReflectionClass; + +/** + * Name generator. + * Generates classname for double. + * + * @author Konstantin Kudryashov + */ +class NameGenerator +{ + private static $counter = 1; + + /** + * Generates name. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + public function name(ReflectionClass $class = null, array $interfaces) + { + $parts = array(); + + if (null !== $class) { + $parts[] = $class->getName(); + } else { + foreach ($interfaces as $interface) { + $parts[] = $interface->getShortName(); + } + } + + if (!count($parts)) { + $parts[] = 'stdClass'; + } + + return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +/** + * Core double interface. + * All doubled classes will implement this one. + * + * @author Konstantin Kudryashov + */ +interface DoubleInterface +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Disable constructor. + * Makes all constructor arguments optional. + * + * @author Konstantin Kudryashov + */ +class DisableConstructorPatch implements ClassPatchInterface +{ + /** + * Checks if class has `__construct` method. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Makes all class constructor arguments optional. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + if (!$node->hasMethod('__construct')) { + $node->addMethod(new MethodNode('__construct', '')); + + return; + } + + $constructor = $node->getMethod('__construct'); + foreach ($constructor->getArguments() as $argument) { + $argument->setDefault(null); + } + + $constructor->setCode(<< + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Exception patch for HHVM to remove the stubs from special methods + * + * @author Christophe Coevoet + */ +class HhvmExceptionPatch implements ClassPatchInterface +{ + /** + * Supports exceptions on HHVM. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (!defined('HHVM_VERSION')) { + return false; + } + + return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception'); + } + + /** + * Removes special exception static methods from the doubled methods. + * + * @param ClassNode $node + * + * @return void + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('setTraceOptions')) { + $node->getMethod('setTraceOptions')->useParentCode(); + } + if ($node->hasMethod('getTraceOptions')) { + $node->getMethod('getTraceOptions')->useParentCode(); + } + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return -50; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Class patch interface. + * Class patches extend doubles functionality or help + * Prophecy to avoid some internal PHP bugs. + * + * @author Konstantin Kudryashov + */ +interface ClassPatchInterface +{ + /** + * Checks if patch supports specific class node. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node); + + /** + * Applies patch to the specific class node. + * + * @param ClassNode $node + * @return void + */ + public function apply(ClassNode $node); + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\Doubler\Generator\Node\ArgumentNode; + +/** + * Add Prophecy functionality to the double. + * This is a core class patch for Prophecy. + * + * @author Konstantin Kudryashov + */ +class ProphecySubjectPatch implements ClassPatchInterface +{ + /** + * Always returns true. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Apply Prophecy functionality to class node. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface'); + $node->addProperty('objectProphecy', 'private'); + + foreach ($node->getMethods() as $name => $method) { + if ('__construct' === strtolower($name)) { + continue; + } + + if ($method->getReturnType() === 'void') { + $method->setCode( + '$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' + ); + } else { + $method->setCode( + 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' + ); + } + } + + $prophecySetter = new MethodNode('setProphecy'); + $prophecyArgument = new ArgumentNode('prophecy'); + $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface'); + $prophecySetter->addArgument($prophecyArgument); + $prophecySetter->setCode('$this->objectProphecy = $prophecy;'); + + $prophecyGetter = new MethodNode('getProphecy'); + $prophecyGetter->setCode('return $this->objectProphecy;'); + + if ($node->hasMethod('__call')) { + $__call = $node->getMethod('__call'); + } else { + $__call = new MethodNode('__call'); + $__call->addArgument(new ArgumentNode('name')); + $__call->addArgument(new ArgumentNode('arguments')); + + $node->addMethod($__call, true); + } + + $__call->setCode(<<getProphecy(), func_get_arg(0) +); +PHP + ); + + $node->addMethod($prophecySetter, true); + $node->addMethod($prophecyGetter, true); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 0; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Remove method functionality from the double which will clash with php keywords. + * + * @author Milan Magudia + */ +class KeywordPatch implements ClassPatchInterface +{ + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Remove methods that clash with php keywords + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $methodNames = array_keys($node->getMethods()); + $methodsToRemove = array_intersect($methodNames, $this->getKeywords()); + foreach ($methodsToRemove as $methodName) { + $node->removeMethod($methodName); + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 49; + } + + /** + * Returns array of php keywords. + * + * @return array + */ + private function getKeywords() + { + if (\PHP_VERSION_ID >= 70000) { + return array('__halt_compiler'); + } + + return array( + '__halt_compiler', + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'eval', + 'exit', + 'extends', + 'final', + 'finally', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'require', + 'require_once', + 'return', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Traversable interface patch. + * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. + * + * @author Konstantin Kudryashov + */ +class TraversablePatch implements ClassPatchInterface +{ + /** + * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (in_array('Iterator', $node->getInterfaces())) { + return false; + } + if (in_array('IteratorAggregate', $node->getInterfaces())) { + return false; + } + + foreach ($node->getInterfaces() as $interface) { + if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) { + continue; + } + if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) { + continue; + } + if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) { + continue; + } + + return true; + } + + return false; + } + + /** + * Forces class to implement Iterator interface. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $node->addInterface('Iterator'); + + $node->addMethod(new MethodNode('current')); + $node->addMethod(new MethodNode('key')); + $node->addMethod(new MethodNode('next')); + $node->addMethod(new MethodNode('rewind')); + $node->addMethod(new MethodNode('valid')); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 100; + } +} +implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); + } + + /** + * @param ClassNode $node + * @return bool + */ + private function implementsAThrowableInterface(ClassNode $node) + { + foreach ($node->getInterfaces() as $type) { + if (is_a($type, 'Throwable', true)) { + return true; + } + } + + return false; + } + + /** + * @param ClassNode $node + * @return bool + */ + private function doesNotExtendAThrowableClass(ClassNode $node) + { + return !is_a($node->getParentClass(), 'Throwable', true); + } + + /** + * Applies patch to the specific class node. + * + * @param ClassNode $node + * + * @return void + */ + public function apply(ClassNode $node) + { + $this->checkItCanBeDoubled($node); + $this->setParentClassToException($node); + } + + private function checkItCanBeDoubled(ClassNode $node) + { + $className = $node->getParentClass(); + if ($className !== 'stdClass') { + throw new ClassCreatorException( + sprintf( + 'Cannot double concrete class %s as well as implement Traversable', + $className + ), + $node + ); + } + } + + private function setParentClassToException(ClassNode $node) + { + $node->setParentClass('Exception'); + + $node->removeMethod('getMessage'); + $node->removeMethod('getCode'); + $node->removeMethod('getFile'); + $node->removeMethod('getLine'); + $node->removeMethod('getTrace'); + $node->removeMethod('getPrevious'); + $node->removeMethod('getNext'); + $node->removeMethod('getTraceAsString'); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 100; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * ReflectionClass::newInstance patch. + * Makes first argument of newInstance optional, since it works but signature is misleading + * + * @author Florian Klein + */ +class ReflectionClassNewInstancePatch implements ClassPatchInterface +{ + /** + * Supports ReflectionClass + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return 'ReflectionClass' === $node->getParentClass(); + } + + /** + * Updates newInstance's first argument to make it optional + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + foreach ($node->getMethod('newInstance')->getArguments() as $argument) { + $argument->setDefault(null); + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher = earlier) + */ + public function getPriority() + { + return 50; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * SplFileInfo patch. + * Makes SplFileInfo and derivative classes usable with Prophecy. + * + * @author Konstantin Kudryashov + */ +class SplFileInfoPatch implements ClassPatchInterface +{ + /** + * Supports everything that extends SplFileInfo. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (null === $node->getParentClass()) { + return false; + } + return 'SplFileInfo' === $node->getParentClass() + || is_subclass_of($node->getParentClass(), 'SplFileInfo') + ; + } + + /** + * Updated constructor code to call parent one with dummy file argument. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('__construct')) { + $constructor = $node->getMethod('__construct'); + } else { + $constructor = new MethodNode('__construct'); + $node->addMethod($constructor); + } + + if ($this->nodeIsDirectoryIterator($node)) { + $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); + + return; + } + + if ($this->nodeIsSplFileObject($node)) { + $filePath = str_replace('\\','\\\\',__FILE__); + $constructor->setCode('return parent::__construct("' . $filePath .'");'); + + return; + } + + if ($this->nodeIsSymfonySplFileInfo($node)) { + $filePath = str_replace('\\','\\\\',__FILE__); + $constructor->setCode('return parent::__construct("' . $filePath .'", "", "");'); + + return; + } + + $constructor->useParentCode(); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 50; + } + + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsDirectoryIterator(ClassNode $node) + { + $parent = $node->getParentClass(); + + return 'DirectoryIterator' === $parent + || is_subclass_of($parent, 'DirectoryIterator'); + } + + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsSplFileObject(ClassNode $node) + { + $parent = $node->getParentClass(); + + return 'SplFileObject' === $parent + || is_subclass_of($parent, 'SplFileObject'); + } + + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsSymfonySplFileInfo(ClassNode $node) + { + $parent = $node->getParentClass(); + + return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; +use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; + +/** + * Discover Magical API using "@method" PHPDoc format. + * + * @author Thomas Tourlourat + * @author Kévin Dunglas + * @author Théo FIDRY + */ +class MagicCallPatch implements ClassPatchInterface +{ + private $tagRetriever; + + public function __construct(MethodTagRetrieverInterface $tagRetriever = null) + { + $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; + } + + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Discover Magical API + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $types = array_filter($node->getInterfaces(), function ($interface) { + return 0 !== strpos($interface, 'Prophecy\\'); + }); + $types[] = $node->getParentClass(); + + foreach ($types as $type) { + $reflectionClass = new \ReflectionClass($type); + + while ($reflectionClass) { + $tagList = $this->tagRetriever->getTagList($reflectionClass); + + foreach ($tagList as $tag) { + $methodName = $tag->getMethodName(); + + if (empty($methodName)) { + continue; + } + + if (!$reflectionClass->hasMethod($methodName)) { + $methodNode = new MethodNode($methodName); + $methodNode->setStatic($tag->isStatic()); + $node->addMethod($methodNode); + } + } + + $reflectionClass = $reflectionClass->getParentClass(); + } + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return integer Priority number (higher - earlier) + */ + public function getPriority() + { + return 50; + } +} + + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use Prophecy\Exception\Doubler\DoubleException; +use Prophecy\Exception\Doubler\ClassNotFoundException; +use Prophecy\Exception\Doubler\InterfaceNotFoundException; +use ReflectionClass; + +/** + * Lazy double. + * Gives simple interface to describe double before creating it. + * + * @author Konstantin Kudryashov + */ +class LazyDouble +{ + private $doubler; + private $class; + private $interfaces = array(); + private $arguments = null; + private $double; + + /** + * Initializes lazy double. + * + * @param Doubler $doubler + */ + public function __construct(Doubler $doubler) + { + $this->doubler = $doubler; + } + + /** + * Tells doubler to use specific class as parent one for double. + * + * @param string|ReflectionClass $class + * + * @throws \Prophecy\Exception\Doubler\ClassNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + */ + public function setParentClass($class) + { + if (null !== $this->double) { + throw new DoubleException('Can not extend class with already instantiated double.'); + } + + if (!$class instanceof ReflectionClass) { + if (!class_exists($class)) { + throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class); + } + + $class = new ReflectionClass($class); + } + + $this->class = $class; + } + + /** + * Tells doubler to implement specific interface with double. + * + * @param string|ReflectionClass $interface + * + * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + */ + public function addInterface($interface) + { + if (null !== $this->double) { + throw new DoubleException( + 'Can not implement interface with already instantiated double.' + ); + } + + if (!$interface instanceof ReflectionClass) { + if (!interface_exists($interface)) { + throw new InterfaceNotFoundException( + sprintf('Interface %s not found.', $interface), + $interface + ); + } + + $interface = new ReflectionClass($interface); + } + + $this->interfaces[] = $interface; + } + + /** + * Sets constructor arguments. + * + * @param array $arguments + */ + public function setArguments(array $arguments = null) + { + $this->arguments = $arguments; + } + + /** + * Creates double instance or returns already created one. + * + * @return DoubleInterface + */ + public function getInstance() + { + if (null === $this->double) { + if (null !== $this->arguments) { + return $this->double = $this->doubler->double( + $this->class, $this->interfaces, $this->arguments + ); + } + + $this->double = $this->doubler->double($this->class, $this->interfaces); + } + + return $this->double; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy; + +use Prophecy\Argument\Token; + +/** + * Argument tokens shortcuts. + * + * @author Konstantin Kudryashov + */ +class Argument +{ + /** + * Checks that argument is exact value or object. + * + * @param mixed $value + * + * @return Token\ExactValueToken + */ + public static function exact($value) + { + return new Token\ExactValueToken($value); + } + + /** + * Checks that argument is of specific type or instance of specific class. + * + * @param string $type Type name (`integer`, `string`) or full class name + * + * @return Token\TypeToken + */ + public static function type($type) + { + return new Token\TypeToken($type); + } + + /** + * Checks that argument object has specific state. + * + * @param string $methodName + * @param mixed $value + * + * @return Token\ObjectStateToken + */ + public static function which($methodName, $value) + { + return new Token\ObjectStateToken($methodName, $value); + } + + /** + * Checks that argument matches provided callback. + * + * @param callable $callback + * + * @return Token\CallbackToken + */ + public static function that($callback) + { + return new Token\CallbackToken($callback); + } + + /** + * Matches any single value. + * + * @return Token\AnyValueToken + */ + public static function any() + { + return new Token\AnyValueToken; + } + + /** + * Matches all values to the rest of the signature. + * + * @return Token\AnyValuesToken + */ + public static function cetera() + { + return new Token\AnyValuesToken; + } + + /** + * Checks that argument matches all tokens + * + * @param mixed ... a list of tokens + * + * @return Token\LogicalAndToken + */ + public static function allOf() + { + return new Token\LogicalAndToken(func_get_args()); + } + + /** + * Checks that argument array or countable object has exact number of elements. + * + * @param integer $value array elements count + * + * @return Token\ArrayCountToken + */ + public static function size($value) + { + return new Token\ArrayCountToken($value); + } + + /** + * Checks that argument array contains (key, value) pair + * + * @param mixed $key exact value or token + * @param mixed $value exact value or token + * + * @return Token\ArrayEntryToken + */ + public static function withEntry($key, $value) + { + return new Token\ArrayEntryToken($key, $value); + } + + /** + * Checks that arguments array entries all match value + * + * @param mixed $value + * + * @return Token\ArrayEveryEntryToken + */ + public static function withEveryEntry($value) + { + return new Token\ArrayEveryEntryToken($value); + } + + /** + * Checks that argument array contains value + * + * @param mixed $value + * + * @return Token\ArrayEntryToken + */ + public static function containing($value) + { + return new Token\ArrayEntryToken(self::any(), $value); + } + + /** + * Checks that argument array has key + * + * @param mixed $key exact value or token + * + * @return Token\ArrayEntryToken + */ + public static function withKey($key) + { + return new Token\ArrayEntryToken($key, self::any()); + } + + /** + * Checks that argument does not match the value|token. + * + * @param mixed $value either exact value or argument token + * + * @return Token\LogicalNotToken + */ + public static function not($value) + { + return new Token\LogicalNotToken($value); + } + + /** + * @param string $value + * + * @return Token\StringContainsToken + */ + public static function containingString($value) + { + return new Token\StringContainsToken($value); + } + + /** + * Checks that argument is identical value. + * + * @param mixed $value + * + * @return Token\IdenticalValueToken + */ + public static function is($value) + { + return new Token\IdenticalValueToken($value); + } + + /** + * Check that argument is same value when rounding to the + * given precision. + * + * @param float $value + * @param float $precision + * + * @return Token\ApproximateValueToken + */ + public static function approximate($value, $precision = 0) + { + return new Token\ApproximateValueToken($value, $precision); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use Prophecy\Prophecy\ProphecyInterface; +use SebastianBergmann\Comparator\ObjectComparator; + +class ProphecyComparator extends ObjectComparator +{ + public function accepts($expected, $actual) + { + return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use SebastianBergmann\Comparator\Factory as BaseFactory; + +/** + * Prophecy comparator factory. + * + * @author Konstantin Kudryashov + */ +final class Factory extends BaseFactory +{ + /** + * @var Factory + */ + private static $instance; + + public function __construct() + { + parent::__construct(); + + $this->register(new ClosureComparator()); + $this->register(new ProphecyComparator()); + } + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new Factory; + } + + return self::$instance; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Closure comparator. + * + * @author Konstantin Kudryashov + */ +final class ClosureComparator extends Comparator +{ + public function accepts($expected, $actual) + { + return is_object($expected) && $expected instanceof \Closure + && is_object($actual) && $actual instanceof \Closure; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + 'all closures are born different' + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsCountException; + +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +class CallTimesPrediction implements PredictionInterface +{ + private $times; + private $util; + + /** + * Initializes prediction. + * + * @param int $times + * @param StringUtil $util + */ + public function __construct($times, StringUtil $util = null) + { + $this->times = intval($times); + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there was exact amount of calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if ($this->times == count($calls)) { + return; + } + + $methodCalls = $object->findProphecyMethodCalls( + $method->getMethodName(), + new ArgumentsWildcard(array(new AnyValuesToken)) + ); + + if (count($calls)) { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but %d were made:\n%s", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + count($calls), + $this->util->stringifyCalls($calls) + ); + } elseif (count($methodCalls)) { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but none were made.\n". + "Recorded `%s(...)` calls:\n%s", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + $method->getMethodName(), + $this->util->stringifyCalls($methodCalls) + ); + } else { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but none were made.", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard() + ); + } + + throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\NoCallsException; + +/** + * Call prediction. + * + * @author Konstantin Kudryashov + */ +class CallPrediction implements PredictionInterface +{ + private $util; + + /** + * Initializes prediction. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there was at least one call. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\NoCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if (count($calls)) { + return; + } + + $methodCalls = $object->findProphecyMethodCalls( + $method->getMethodName(), + new ArgumentsWildcard(array(new AnyValuesToken)) + ); + + if (count($methodCalls)) { + throw new NoCallsException(sprintf( + "No calls have been made that match:\n". + " %s->%s(%s)\n". + "but expected at least one.\n". + "Recorded `%s(...)` calls:\n%s", + + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + $method->getMethodName(), + $this->util->stringifyCalls($methodCalls) + ), $method); + } + + throw new NoCallsException(sprintf( + "No calls have been made that match:\n". + " %s->%s(%s)\n". + "but expected at least one.", + + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard() + ), $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; + +/** + * Callback prediction. + * + * @author Konstantin Kudryashov + */ +class CallbackPrediction implements PredictionInterface +{ + private $callback; + + /** + * Initializes callback prediction. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackPrediction, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Executes preset callback. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + $callback = $this->callback; + + if ($callback instanceof Closure && method_exists('Closure', 'bind')) { + $callback = Closure::bind($callback, $object); + } + + call_user_func($callback, $calls, $object, $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsException; + +/** + * No calls prediction. + * + * @author Konstantin Kudryashov + */ +class NoCallsPrediction implements PredictionInterface +{ + private $util; + + /** + * Initializes prediction. + * + * @param null|StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there were no calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if (!count($calls)) { + return; + } + + $verb = count($calls) === 1 ? 'was' : 'were'; + + throw new UnexpectedCallsException(sprintf( + "No calls expected that match:\n". + " %s->%s(%s)\n". + "but %d %s made:\n%s", + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + count($calls), + $verb, + $this->util->stringifyCalls($calls) + ), $method, $calls); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PredictionInterface +{ + /** + * Tests that double fulfilled prediction. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + * @return void + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy; + +use Prophecy\Doubler\Doubler; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Doubler\ClassPatch; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\RevealerInterface; +use Prophecy\Prophecy\Revealer; +use Prophecy\Call\CallCenter; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Exception\Prediction\AggregateException; + +/** + * Prophet creates prophecies. + * + * @author Konstantin Kudryashov + */ +class Prophet +{ + private $doubler; + private $revealer; + private $util; + + /** + * @var ObjectProphecy[] + */ + private $prophecies = array(); + + /** + * Initializes Prophet. + * + * @param null|Doubler $doubler + * @param null|RevealerInterface $revealer + * @param null|StringUtil $util + */ + public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, + StringUtil $util = null) + { + if (null === $doubler) { + $doubler = new Doubler; + $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch); + $doubler->registerClassPatch(new ClassPatch\TraversablePatch); + $doubler->registerClassPatch(new ClassPatch\ThrowablePatch); + $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch); + $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch); + $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch); + $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); + $doubler->registerClassPatch(new ClassPatch\MagicCallPatch); + $doubler->registerClassPatch(new ClassPatch\KeywordPatch); + } + + $this->doubler = $doubler; + $this->revealer = $revealer ?: new Revealer; + $this->util = $util ?: new StringUtil; + } + + /** + * Creates new object prophecy. + * + * @param null|string $classOrInterface Class or interface name + * + * @return ObjectProphecy + */ + public function prophesize($classOrInterface = null) + { + $this->prophecies[] = $prophecy = new ObjectProphecy( + new LazyDouble($this->doubler), + new CallCenter($this->util), + $this->revealer + ); + + if ($classOrInterface && class_exists($classOrInterface)) { + return $prophecy->willExtend($classOrInterface); + } + + if ($classOrInterface && interface_exists($classOrInterface)) { + return $prophecy->willImplement($classOrInterface); + } + + return $prophecy; + } + + /** + * Returns all created object prophecies. + * + * @return ObjectProphecy[] + */ + public function getProphecies() + { + return $this->prophecies; + } + + /** + * Returns Doubler instance assigned to this Prophet. + * + * @return Doubler + */ + public function getDoubler() + { + return $this->doubler; + } + + /** + * Checks all predictions defined by prophecies of this Prophet. + * + * @throws Exception\Prediction\AggregateException If any prediction fails + */ + public function checkPredictions() + { + $exception = new AggregateException("Some predictions failed:\n"); + foreach ($this->prophecies as $prophecy) { + try { + $prophecy->checkProphecyMethodsPredictions(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + + if (count($exception->getExceptions())) { + throw $exception; + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use phpDocumentor\Reflection\DocBlock\Tags\Method; + +/** + * @author Théo FIDRY + * + * @internal + */ +final class ClassAndInterfaceTagRetriever implements MethodTagRetrieverInterface +{ + private $classRetriever; + + public function __construct(MethodTagRetrieverInterface $classRetriever = null) + { + if (null !== $classRetriever) { + $this->classRetriever = $classRetriever; + + return; + } + + $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory') + ? new ClassTagRetriever() + : new LegacyClassTagRetriever() + ; + } + + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass) + { + return array_merge( + $this->classRetriever->getTagList($reflectionClass), + $this->getInterfacesTagList($reflectionClass) + ); + } + + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + private function getInterfacesTagList(\ReflectionClass $reflectionClass) + { + $interfaces = $reflectionClass->getInterfaces(); + $tagList = array(); + + foreach($interfaces as $interface) { + $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface)); + } + + return $tagList; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tags\Method; +use phpDocumentor\Reflection\DocBlockFactory; +use phpDocumentor\Reflection\Types\ContextFactory; + +/** + * @author Théo FIDRY + * + * @internal + */ +final class ClassTagRetriever implements MethodTagRetrieverInterface +{ + private $docBlockFactory; + private $contextFactory; + + public function __construct() + { + $this->docBlockFactory = DocBlockFactory::createInstance(); + $this->contextFactory = new ContextFactory(); + } + + /** + * @param \ReflectionClass $reflectionClass + * + * @return Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass) + { + try { + $phpdoc = $this->docBlockFactory->create( + $reflectionClass, + $this->contextFactory->createFromReflector($reflectionClass) + ); + + return $phpdoc->getTagsByName('method'); + } catch (\InvalidArgumentException $e) { + return array(); + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; +use phpDocumentor\Reflection\DocBlock\Tags\Method; + +/** + * @author Théo FIDRY + * + * @internal + */ +interface MethodTagRetrieverInterface +{ + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[]|Method[] + */ + public function getTagList(\ReflectionClass $reflectionClass); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\PhpDocumentor; + +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; + +/** + * @author Théo FIDRY + * + * @internal + */ +final class LegacyClassTagRetriever implements MethodTagRetrieverInterface +{ + /** + * @param \ReflectionClass $reflectionClass + * + * @return LegacyMethodTag[] + */ + public function getTagList(\ReflectionClass $reflectionClass) + { + $phpdoc = new DocBlock($reflectionClass->getDocComment()); + + return $phpdoc->getTagsByName('method'); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * This class is a modification from sebastianbergmann/exporter + * @see https://github.com/sebastianbergmann/exporter + */ +class ExportUtil +{ + /** + * Exports a value as a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value + * @param int $indentation The indentation level of the 2nd+ line + * @return string + */ + public static function export($value, $indentation = 0) + { + return self::recursiveExport($value, $indentation); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param mixed $value + * @return array + */ + public static function toArray($value) + { + if (!is_object($value)) { + return (array) $value; + } + + $array = array(); + + foreach ((array) $value as $key => $val) { + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + + $array[$key] = $val; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof \SplObjectStorage) { + // However, the fast method does work in HHVM, and exposes the + // internal implementation. Hide it again. + if (property_exists('\SplObjectStorage', '__storage')) { + unset($array['__storage']); + } elseif (property_exists('\SplObjectStorage', 'storage')) { + unset($array['storage']); + } + + if (property_exists('\SplObjectStorage', '__key')) { + unset($array['__key']); + } + + foreach ($value as $key => $val) { + $array[spl_object_hash($val)] = array( + 'obj' => $val, + 'inf' => $value->getInfo(), + ); + } + } + + return $array; + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected static function recursiveExport(&$value, $indentation, $processed = null) + { + if ($value === null) { + return 'null'; + } + + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + if (is_float($value) && floatval(intval($value)) === $value) { + return "$value.0"; + } + + if (is_resource($value)) { + return sprintf( + 'resource(%d) of type (%s)', + $value, + get_resource_type($value) + ); + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . + "'"; + } + + $whitespace = str_repeat(' ', 4 * $indentation); + + if (!$processed) { + $processed = new Context; + } + + if (is_array($value)) { + if (($key = $processed->contains($value)) !== false) { + return 'Array &' . $key; + } + + $array = $value; + $key = $processed->add($value); + $values = ''; + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + self::recursiveExport($k, $indentation), + self::recursiveExport($value[$k], $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('Array &%s (%s)', $key, $values); + } + + if (is_object($value)) { + $class = get_class($value); + + if ($value instanceof ProphecyInterface) { + return sprintf('%s Object (*Prophecy*)', $class); + } elseif ($hash = $processed->contains($value)) { + return sprintf('%s:%s Object', $class, $hash); + } + + $hash = $processed->add($value); + $values = ''; + $array = self::toArray($value); + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + self::recursiveExport($k, $indentation), + self::recursiveExport($v, $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('%s:%s Object (%s)', $class, $hash, $values); + } + + return var_export($value, true); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Util; + +use Prophecy\Call\Call; + +/** + * String utility. + * + * @author Konstantin Kudryashov + */ +class StringUtil +{ + private $verbose; + + /** + * @param bool $verbose + */ + public function __construct($verbose = true) + { + $this->verbose = $verbose; + } + + /** + * Stringifies any provided value. + * + * @param mixed $value + * @param boolean $exportObject + * + * @return string + */ + public function stringify($value, $exportObject = true) + { + if (is_array($value)) { + if (range(0, count($value) - 1) === array_keys($value)) { + return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']'; + } + + $stringify = array($this, __FUNCTION__); + + return '['.implode(', ', array_map(function ($item, $key) use ($stringify) { + return (is_integer($key) ? $key : '"'.$key.'"'). + ' => '.call_user_func($stringify, $item); + }, $value, array_keys($value))).']'; + } + if (is_resource($value)) { + return get_resource_type($value).':'.$value; + } + if (is_object($value)) { + return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value)); + } + if (true === $value || false === $value) { + return $value ? 'true' : 'false'; + } + if (is_string($value)) { + $str = sprintf('"%s"', str_replace("\n", '\\n', $value)); + + if (!$this->verbose && 50 <= strlen($str)) { + return substr($str, 0, 50).'"...'; + } + + return $str; + } + if (null === $value) { + return 'null'; + } + + return (string) $value; + } + + /** + * Stringifies provided array of calls. + * + * @param Call[] $calls Array of Call instances + * + * @return string + */ + public function stringifyCalls(array $calls) + { + $self = $this; + + return implode(PHP_EOL, array_map(function (Call $call) use ($self) { + return sprintf(' - %s(%s) @ %s', + $call->getMethodName(), + implode(', ', array_map(array($self, 'stringify'), $call->getArguments())), + str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace()) + ); + }, $calls)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Call; + +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Call\UnexpectedCallException; + +/** + * Calls receiver & manager. + * + * @author Konstantin Kudryashov + */ +class CallCenter +{ + private $util; + + /** + * @var Call[] + */ + private $recordedCalls = array(); + + /** + * Initializes call center. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Makes and records specific method call for object prophecy. + * + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments + * + * @return mixed Returns null if no promise for prophecy found or promise return value. + * + * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found + */ + public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) + { + // For efficiency exclude 'args' from the generated backtrace + if (PHP_VERSION_ID >= 50400) { + // Limit backtrace to last 3 calls as we don't use the rest + // Limit argument was introduced in PHP 5.4.0 + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); + } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { + // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6 + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + } else { + $backtrace = debug_backtrace(); + } + + $file = $line = null; + if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { + $file = $backtrace[2]['file']; + $line = $backtrace[2]['line']; + } + + // If no method prophecies defined, then it's a dummy, so we'll just return null + if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) { + $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); + + return null; + } + + // There are method prophecies, so it's a fake/stub. Searching prophecy for this call + $matches = array(); + foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { + if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { + $matches[] = array($score, $methodProphecy); + } + } + + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!count($matches)) { + throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments); + } + + // Sort matches by their score value + @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); + + $score = $matches[0][0]; + // If Highest rated method prophecy has a promise - execute it or return null instead + $methodProphecy = $matches[0][1]; + $returnValue = null; + $exception = null; + if ($promise = $methodProphecy->getPromise()) { + try { + $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); + } catch (\Exception $e) { + $exception = $e; + } + } + + if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { + throw new MethodProphecyException( + "The method \"$methodName\" has a void return type, but the promise returned a value", + $methodProphecy + ); + } + + $this->recordedCalls[] = $call = new Call( + $methodName, $arguments, $returnValue, $exception, $file, $line + ); + $call->addScore($methodProphecy->getArgumentsWildcard(), $score); + + if (null !== $exception) { + throw $exception; + } + + return $returnValue; + } + + /** + * Searches for calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findCalls($methodName, ArgumentsWildcard $wildcard) + { + return array_values( + array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { + return $methodName === $call->getMethodName() + && 0 < $call->getScore($wildcard) + ; + }) + ); + } + + private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, + array $arguments) + { + $classname = get_class($prophecy->reveal()); + $indentationLength = 8; // looks good + $argstring = implode( + ",\n", + $this->indentArguments( + array_map(array($this->util, 'stringify'), $arguments), + $indentationLength + ) + ); + + $expected = array(); + + foreach (call_user_func_array('array_merge', $prophecy->getMethodProphecies()) as $methodProphecy) { + $expected[] = sprintf( + " - %s(\n" . + "%s\n" . + " )", + $methodProphecy->getMethodName(), + implode( + ",\n", + $this->indentArguments( + array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), + $indentationLength + ) + ) + ); + } + + return new UnexpectedCallException( + sprintf( + "Unexpected method call on %s:\n". + " - %s(\n". + "%s\n". + " )\n". + "expected calls were:\n". + "%s", + + $classname, $methodName, $argstring, implode("\n", $expected) + ), + $prophecy, $methodName, $arguments + + ); + } + + private function formatExceptionMessage(MethodProphecy $methodProphecy) + { + return sprintf( + " - %s(\n". + "%s\n". + " )", + $methodProphecy->getMethodName(), + implode( + ",\n", + $this->indentArguments( + array_map( + function ($token) { + return (string) $token; + }, + $methodProphecy->getArgumentsWildcard()->getTokens() + ), + $indentationLength + ) + ) + ); + } + + private function indentArguments(array $arguments, $indentationLength) + { + return preg_replace_callback( + '/^/m', + function () use ($indentationLength) { + return str_repeat(' ', $indentationLength); + }, + $arguments + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Call; + +use Exception; +use Prophecy\Argument\ArgumentsWildcard; + +/** + * Call object. + * + * @author Konstantin Kudryashov + */ +class Call +{ + private $methodName; + private $arguments; + private $returnValue; + private $exception; + private $file; + private $line; + private $scores; + + /** + * Initializes call. + * + * @param string $methodName + * @param array $arguments + * @param mixed $returnValue + * @param Exception $exception + * @param null|string $file + * @param null|int $line + */ + public function __construct($methodName, array $arguments, $returnValue, + Exception $exception = null, $file, $line) + { + $this->methodName = $methodName; + $this->arguments = $arguments; + $this->returnValue = $returnValue; + $this->exception = $exception; + $this->scores = new \SplObjectStorage(); + + if ($file) { + $this->file = $file; + $this->line = intval($line); + } + } + + /** + * Returns called method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * Returns called method arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns called method return value. + * + * @return null|mixed + */ + public function getReturnValue() + { + return $this->returnValue; + } + + /** + * Returns exception that call thrown. + * + * @return null|Exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Returns callee filename. + * + * @return string + */ + public function getFile() + { + return $this->file; + } + + /** + * Returns callee line number. + * + * @return int + */ + public function getLine() + { + return $this->line; + } + + /** + * Returns short notation for callee place. + * + * @return string + */ + public function getCallPlace() + { + if (null === $this->file) { + return 'unknown'; + } + + return sprintf('%s:%d', $this->file, $this->line); + } + + /** + * Adds the wildcard match score for the provided wildcard. + * + * @param ArgumentsWildcard $wildcard + * @param false|int $score + * + * @return $this + */ + public function addScore(ArgumentsWildcard $wildcard, $score) + { + $this->scores[$wildcard] = $score; + + return $this; + } + + /** + * Returns wildcard match score for the provided wildcard. The score is + * calculated if not already done. + * + * @param ArgumentsWildcard $wildcard + * + * @return false|int False OR integer score (higher - better) + */ + public function getScore(ArgumentsWildcard $wildcard) + { + if (isset($this->scores[$wildcard])) { + return $this->scores[$wildcard]; + } + + return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return promise. + * + * @author Konstantin Kudryashov + */ +class ReturnPromise implements PromiseInterface +{ + private $returnValues = array(); + + /** + * Initializes promise. + * + * @param array $returnValues Array of values + */ + public function __construct(array $returnValues) + { + $this->returnValues = $returnValues; + } + + /** + * Returns saved values one by one until last one, then continuously returns last value. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + $value = array_shift($this->returnValues); + + if (!count($this->returnValues)) { + $this->returnValues[] = $value; + } + + return $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Promise interface. + * Promises are logical blocks, tied to `will...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PromiseInterface +{ + /** + * Evaluates promise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; + +/** + * Callback promise. + * + * @author Konstantin Kudryashov + */ +class CallbackPromise implements PromiseInterface +{ + private $callback; + + /** + * Initializes callback promise. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackPromise, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Evaluates promise callback. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + $callback = $this->callback; + + if ($callback instanceof Closure && method_exists('Closure', 'bind')) { + $callback = Closure::bind($callback, $object); + } + + return call_user_func($callback, $args, $object, $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return argument promise. + * + * @author Konstantin Kudryashov + */ +class ReturnArgumentPromise implements PromiseInterface +{ + /** + * @var int + */ + private $index; + + /** + * Initializes callback promise. + * + * @param int $index The zero-indexed number of the argument to return + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($index = 0) + { + if (!is_int($index) || $index < 0) { + throw new InvalidArgumentException(sprintf( + 'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', + $index + )); + } + $this->index = $index; + } + + /** + * Returns nth argument if has one, null otherwise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return null|mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + return count($args) > $this->index ? $args[$this->index] : null; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Throw promise. + * + * @author Konstantin Kudryashov + */ +class ThrowPromise implements PromiseInterface +{ + private $exception; + + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * Initializes promise. + * + * @param string|\Exception|\Throwable $exception Exception class name or instance + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($exception) + { + if (is_string($exception)) { + if (!class_exists($exception) || !$this->isAValidThrowable($exception)) { + throw new InvalidArgumentException(sprintf( + 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', + $exception + )); + } + } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { + throw new InvalidArgumentException(sprintf( + 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', + is_object($exception) ? get_class($exception) : gettype($exception) + )); + } + + $this->exception = $exception; + } + + /** + * Throws predefined exception. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + if (is_string($this->exception)) { + $classname = $this->exception; + $reflection = new ReflectionClass($classname); + $constructor = $reflection->getConstructor(); + + if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { + throw $reflection->newInstance(); + } + + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + + throw $this->instantiator->instantiate($classname); + } + + throw $this->exception; + } + + /** + * @param string $exception + * + * @return bool + */ + private function isAValidThrowable($exception) + { + return is_a($exception, 'Exception', true) || is_subclass_of($exception, 'Throwable', true); + } +} +Copyright (c) 2013 Konstantin Kudryashov + Marcello Duarte + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares Exception instances for equality. + */ +class ExceptionComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \Exception && $actual instanceof \Exception; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset( + $array['file'], + $array['line'], + $array['trace'], + $array['string'], + $array['xdebug_message'] + ); + + return $array; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares numerical values for equality. + */ +class NumericComparator extends ScalarComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + // all numerical values, but not if one of them is a double + // or both of them are strings + return \is_numeric($expected) && \is_numeric($actual) && + !(\is_float($expected) || \is_float($actual)) && + !(\is_string($expected) && \is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (\is_infinite($actual) && \is_infinite($expected)) { + return; // @codeCoverageIgnore + } + + if ((\is_infinite($actual) xor \is_infinite($expected)) || + (\is_nan($actual) || \is_nan($expected)) || + \abs($actual - $expected) > $delta) { + throw new ComparisonFailure( + $expected, + $actual, + '', + '', + false, + \sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. + */ +class MockObjectComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return ($expected instanceof \PHPUnit_Framework_MockObject_MockObject || $expected instanceof \PHPUnit\Framework\MockObject\MockObject) && + ($actual instanceof \PHPUnit_Framework_MockObject_MockObject || $actual instanceof \PHPUnit\Framework\MockObject\MockObject); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset($array['__phpunit_invocationMocker']); + + return $array; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares values for type equality. + */ +class TypeComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return true; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (\gettype($expected) != \gettype($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + \sprintf( + '%s does not match expected type "%s".', + $this->exporter->shortenedExport($actual), + \gettype($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares doubles for equality. + */ +class DoubleComparator extends NumericComparator +{ + /** + * Smallest value available in PHP. + * + * @var float + */ + const EPSILON = 0.0000000001; + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return (\is_float($expected) || \is_float($actual)) && \is_numeric($expected) && \is_numeric($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($delta == 0) { + $delta = self::EPSILON; + } + + parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Exporter\Exporter; + +/** + * Abstract base class for comparators which compare values for equality. + */ +abstract class Comparator +{ + /** + * @var Factory + */ + protected $factory; + + /** + * @var Exporter + */ + protected $exporter; + + public function __construct() + { + $this->exporter = new Exporter; + } + + public function setFactory(Factory $factory) + { + $this->factory = $factory; + } + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + abstract public function accepts($expected, $actual); + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares arrays for equality. + */ +class ArrayComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return \is_array($expected) && \is_array($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) + { + if ($canonicalize) { + \sort($expected); + \sort($actual); + } + + $remaining = $actual; + $actualAsString = "Array (\n"; + $expectedAsString = "Array (\n"; + $equal = true; + + foreach ($expected as $key => $value) { + unset($remaining[$key]); + + if (!\array_key_exists($key, $actual)) { + $expectedAsString .= \sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + + continue; + } + + try { + $comparator = $this->factory->getComparatorFor($value, $actual[$key]); + $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); + + $expectedAsString .= \sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $actualAsString .= \sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($actual[$key]) + ); + } catch (ComparisonFailure $e) { + $expectedAsString .= \sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected()) + ); + + $actualAsString .= \sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual()) + ); + + $equal = false; + } + } + + foreach ($remaining as $key => $value) { + $actualAsString .= \sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + } + + $expectedAsString .= ')'; + $actualAsString .= ')'; + + if (!$equal) { + throw new ComparisonFailure( + $expected, + $actual, + $expectedAsString, + $actualAsString, + false, + 'Failed asserting that two arrays are equal.' + ); + } + } + + protected function indent($lines) + { + return \trim(\str_replace("\n", "\n ", $lines)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Diff\Differ; +use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; + +/** + * Thrown when an assertion for string equality failed. + */ +class ComparisonFailure extends \RuntimeException +{ + /** + * Expected value of the retrieval which does not match $actual. + * + * @var mixed + */ + protected $expected; + + /** + * Actually retrieved value which does not match $expected. + * + * @var mixed + */ + protected $actual; + + /** + * The string representation of the expected value + * + * @var string + */ + protected $expectedAsString; + + /** + * The string representation of the actual value + * + * @var string + */ + protected $actualAsString; + + /** + * @var bool + */ + protected $identical; + + /** + * Optional message which is placed in front of the first line + * returned by toString(). + * + * @var string + */ + protected $message; + + /** + * Initialises with the expected value and the actual value. + * + * @param mixed $expected expected value retrieved + * @param mixed $actual actual value retrieved + * @param string $expectedAsString + * @param string $actualAsString + * @param bool $identical + * @param string $message a string which is prefixed on all returned lines + * in the difference output + */ + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '') + { + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; + } + + public function getActual() + { + return $this->actual; + } + + public function getExpected() + { + return $this->expected; + } + + /** + * @return string + */ + public function getActualAsString() + { + return $this->actualAsString; + } + + /** + * @return string + */ + public function getExpectedAsString() + { + return $this->expectedAsString; + } + + /** + * @return string + */ + public function getDiff() + { + if (!$this->actualAsString && !$this->expectedAsString) { + return ''; + } + + $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); + + return $differ->diff($this->expectedAsString, $this->actualAsString); + } + + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares resources for equality. + */ +class ResourceComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return \is_resource($expected) && \is_resource($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($actual != $expected) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Factory for comparators which compare values for equality. + */ +class Factory +{ + /** + * @var Factory + */ + private static $instance; + + /** + * @var Comparator[] + */ + private $customComparators = []; + + /** + * @var Comparator[] + */ + private $defaultComparators = []; + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new self; + } + + return self::$instance; + } + + /** + * Constructs a new factory. + */ + public function __construct() + { + $this->registerDefaultComparators(); + } + + /** + * Returns the correct comparator for comparing two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return Comparator + */ + public function getComparatorFor($expected, $actual) + { + foreach ($this->customComparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + + foreach ($this->defaultComparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + } + + /** + * Registers a new comparator. + * + * This comparator will be returned by getComparatorFor() if its accept() method + * returns TRUE for the compared values. It has higher priority than the + * existing comparators, meaning that its accept() method will be invoked + * before those of the other comparators. + * + * @param Comparator $comparator The comparator to be registered + */ + public function register(Comparator $comparator) + { + \array_unshift($this->customComparators, $comparator); + + $comparator->setFactory($this); + } + + /** + * Unregisters a comparator. + * + * This comparator will no longer be considered by getComparatorFor(). + * + * @param Comparator $comparator The comparator to be unregistered + */ + public function unregister(Comparator $comparator) + { + foreach ($this->customComparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->customComparators[$key]); + } + } + } + + /** + * Unregisters all non-default comparators. + */ + public function reset() + { + $this->customComparators = []; + } + + private function registerDefaultComparators() + { + $this->registerDefaultComparator(new MockObjectComparator); + $this->registerDefaultComparator(new DateTimeComparator); + $this->registerDefaultComparator(new DOMNodeComparator); + $this->registerDefaultComparator(new SplObjectStorageComparator); + $this->registerDefaultComparator(new ExceptionComparator); + $this->registerDefaultComparator(new ObjectComparator); + $this->registerDefaultComparator(new ResourceComparator); + $this->registerDefaultComparator(new ArrayComparator); + $this->registerDefaultComparator(new DoubleComparator); + $this->registerDefaultComparator(new NumericComparator); + $this->registerDefaultComparator(new ScalarComparator); + $this->registerDefaultComparator(new TypeComparator); + } + + private function registerDefaultComparator(Comparator $comparator) + { + $this->defaultComparators[] = $comparator; + + $comparator->setFactory($this); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +use DOMDocument; +use DOMNode; + +/** + * Compares DOMNode instances for equality. + */ +class DOMNodeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof DOMNode && $actual instanceof DOMNode; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) + { + $expectedAsString = $this->nodeToText($expected, true, $ignoreCase); + $actualAsString = $this->nodeToText($actual, true, $ignoreCase); + + if ($expectedAsString !== $actualAsString) { + $type = $expected instanceof DOMDocument ? 'documents' : 'nodes'; + + throw new ComparisonFailure( + $expected, + $actual, + $expectedAsString, + $actualAsString, + false, + \sprintf("Failed asserting that two DOM %s are equal.\n", $type) + ); + } + } + + /** + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMNode. + */ + private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string + { + if ($canonicalize) { + $document = new DOMDocument; + @$document->loadXML($node->C14N()); + + $node = $document; + } + + $document = $node instanceof DOMDocument ? $node : $node->ownerDocument; + + $document->formatOutput = true; + $document->normalizeDocument(); + + $text = $node instanceof DOMDocument ? $node->saveXML() : $document->saveXML($node); + + return $ignoreCase ? \strtolower($text) : $text; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares \SplObjectStorage instances for equality. + */ +class SplObjectStorageComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares objects for equality. + */ +class ObjectComparator extends ArrayComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return \is_object($expected) && \is_object($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) + { + if (\get_class($actual) !== \get_class($expected)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + \sprintf( + '%s is not instance of expected class "%s".', + $this->exporter->export($actual), + \get_class($expected) + ) + ); + } + + // don't compare twice to allow for cyclic dependencies + if (\in_array([$actual, $expected], $processed, true) || + \in_array([$expected, $actual], $processed, true)) { + return; + } + + $processed[] = [$actual, $expected]; + + // don't compare objects if they are identical + // this helps to avoid the error "maximum function nesting level reached" + // CAUTION: this conditional clause is not tested + if ($actual !== $expected) { + try { + parent::assertEquals( + $this->toArray($expected), + $this->toArray($actual), + $delta, + $canonicalize, + $ignoreCase, + $processed + ); + } catch (ComparisonFailure $e) { + throw new ComparisonFailure( + $expected, + $actual, + // replace "Array" with "MyClass object" + \substr_replace($e->getExpectedAsString(), \get_class($expected) . ' Object', 0, 5), + \substr_replace($e->getActualAsString(), \get_class($actual) . ' Object', 0, 5), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + protected function toArray($object) + { + return $this->exporter->toArray($object); + } +} +Comparator + +Copyright (c) 2002-2018, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares DateTimeInterface instances for equality. + */ +class DateTimeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + */ + public function accepts($expected, $actual) + { + return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && + ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * @param array $processed List of already processed elements (used to prevent infinite recursion) + * + * @throws \Exception + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) + { + /** @var \DateTimeInterface $expected */ + /** @var \DateTimeInterface $actual */ + $absDelta = \abs($delta); + $delta = new \DateInterval(\sprintf('PT%dS', $absDelta)); + $delta->f = $absDelta - \floor($absDelta); + + $actualClone = (clone $actual) + ->setTimezone(new \DateTimeZone('UTC')); + + $expectedLower = (clone $expected) + ->setTimezone(new \DateTimeZone('UTC')) + ->sub($delta); + + $expectedUpper = (clone $expected) + ->setTimezone(new \DateTimeZone('UTC')) + ->add($delta); + + if ($actualClone < $expectedLower || $actualClone > $expectedUpper) { + throw new ComparisonFailure( + $expected, + $actual, + $this->dateTimeToString($expected), + $this->dateTimeToString($actual), + false, + 'Failed asserting that two DateTime objects are equal.' + ); + } + } + + /** + * Returns an ISO 8601 formatted string representation of a datetime or + * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly + * initialized. + */ + private function dateTimeToString(\DateTimeInterface $datetime): string + { + $string = $datetime->format('Y-m-d\TH:i:s.uO'); + + return $string ?: 'Invalid DateTimeInterface object'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Comparator; + +/** + * Compares scalar or NULL values for equality. + */ +class ScalarComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * + * @return bool + * + * @since Method available since Release 3.6.0 + */ + public function accepts($expected, $actual) + { + return ((\is_scalar($expected) xor null === $expected) && + (\is_scalar($actual) xor null === $actual)) + // allow comparison between strings and objects featuring __toString() + || (\is_string($expected) && \is_object($actual) && \method_exists($actual, '__toString')) + || (\is_object($expected) && \method_exists($expected, '__toString') && \is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected First value to compare + * @param mixed $actual Second value to compare + * @param float $delta Allowed numerical distance between two values to consider them equal + * @param bool $canonicalize Arrays are sorted before comparison when set to true + * @param bool $ignoreCase Case is ignored when set to true + * + * @throws ComparisonFailure + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedToCompare = $expected; + $actualToCompare = $actual; + + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (\is_string($expected) || \is_string($actual)) { + $expectedToCompare = (string) $expectedToCompare; + $actualToCompare = (string) $actualToCompare; + + if ($ignoreCase) { + $expectedToCompare = \strtolower($expectedToCompare); + $actualToCompare = \strtolower($actualToCompare); + } + } + + if ($expectedToCompare !== $actualToCompare && \is_string($expected) && \is_string($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two strings are equal.' + ); + } + + if ($expectedToCompare != $actualToCompare) { + throw new ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + false, + \sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} +line = $line; + $this->name = $name; + $this->value = $value; + } + + /** + * @return int + */ + public function getLine(): int { + return $this->line; + } + + /** + * @return string + */ + public function getName(): string { + return $this->name; + } + + /** + * @return string + */ + public function getValue(): string { + return $this->value; + } + +} +xmlns = $xmlns; + } + + /** + * @param TokenCollection $tokens + * + * @return DOMDocument + */ + public function toDom(TokenCollection $tokens): DOMDocument { + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = false; + $dom->loadXML($this->toXML($tokens)); + + return $dom; + } + + /** + * @param TokenCollection $tokens + * + * @return string + */ + public function toXML(TokenCollection $tokens): string { + $this->writer = new \XMLWriter(); + $this->writer->openMemory(); + $this->writer->setIndent(true); + $this->writer->startDocument(); + $this->writer->startElement('source'); + $this->writer->writeAttribute('xmlns', $this->xmlns->asString()); + $this->writer->startElement('line'); + $this->writer->writeAttribute('no', '1'); + + $this->previousToken = $tokens[0]; + foreach ($tokens as $token) { + $this->addToken($token); + } + + $this->writer->endElement(); + $this->writer->endElement(); + $this->writer->endDocument(); + + return $this->writer->outputMemory(); + } + + /** + * @param Token $token + */ + private function addToken(Token $token) { + if ($this->previousToken->getLine() < $token->getLine()) { + $this->writer->endElement(); + + $this->writer->startElement('line'); + $this->writer->writeAttribute('no', (string)$token->getLine()); + $this->previousToken = $token; + } + + if ($token->getValue() !== '') { + $this->writer->startElement('token'); + $this->writer->writeAttribute('name', $token->getName()); + $this->writer->writeRaw(htmlspecialchars($token->getValue(), ENT_NOQUOTES | ENT_DISALLOWED | ENT_XML1)); + $this->writer->endElement(); + } + } +} +tokens[] = $token; + } + + /** + * @return Token + */ + public function current(): Token { + return current($this->tokens); + } + + /** + * @return int + */ + public function key(): int { + return key($this->tokens); + } + + /** + * @return void + */ + public function next() { + next($this->tokens); + $this->pos++; + } + + /** + * @return bool + */ + public function valid(): bool { + return $this->count() > $this->pos; + } + + /** + * @return void + */ + public function rewind() { + reset($this->tokens); + $this->pos = 0; + } + + /** + * @return int + */ + public function count(): int { + return count($this->tokens); + } + + /** + * @param mixed $offset + * + * @return bool + */ + public function offsetExists($offset): bool { + return isset($this->tokens[$offset]); + } + + /** + * @param mixed $offset + * + * @return Token + * @throws TokenCollectionException + */ + public function offsetGet($offset): Token { + if (!$this->offsetExists($offset)) { + throw new TokenCollectionException( + sprintf('No Token at offest %s', $offset) + ); + } + + return $this->tokens[$offset]; + } + + /** + * @param mixed $offset + * @param Token $value + * + * @throws TokenCollectionException + */ + public function offsetSet($offset, $value) { + if (!is_int($offset)) { + $type = gettype($offset); + throw new TokenCollectionException( + sprintf( + 'Offset must be of type integer, %s given', + $type === 'object' ? get_class($value) : $type + ) + ); + } + if (!$value instanceof Token) { + $type = gettype($value); + throw new TokenCollectionException( + sprintf( + 'Value must be of type %s, %s given', + Token::class, + $type === 'object' ? get_class($value) : $type + ) + ); + } + $this->tokens[$offset] = $value; + } + + /** + * @param mixed $offset + */ + public function offsetUnset($offset) { + unset($this->tokens[$offset]); + } + +} + 'T_OPEN_BRACKET', + ')' => 'T_CLOSE_BRACKET', + '[' => 'T_OPEN_SQUARE', + ']' => 'T_CLOSE_SQUARE', + '{' => 'T_OPEN_CURLY', + '}' => 'T_CLOSE_CURLY', + ';' => 'T_SEMICOLON', + '.' => 'T_DOT', + ',' => 'T_COMMA', + '=' => 'T_EQUAL', + '<' => 'T_LT', + '>' => 'T_GT', + '+' => 'T_PLUS', + '-' => 'T_MINUS', + '*' => 'T_MULT', + '/' => 'T_DIV', + '?' => 'T_QUESTION_MARK', + '!' => 'T_EXCLAMATION_MARK', + ':' => 'T_COLON', + '"' => 'T_DOUBLE_QUOTES', + '@' => 'T_AT', + '&' => 'T_AMPERSAND', + '%' => 'T_PERCENT', + '|' => 'T_PIPE', + '$' => 'T_DOLLAR', + '^' => 'T_CARET', + '~' => 'T_TILDE', + '`' => 'T_BACKTICK' + ]; + + public function parse(string $source): TokenCollection { + $result = new TokenCollection(); + $tokens = token_get_all($source); + + $lastToken = new Token( + $tokens[0][2], + 'Placeholder', + '' + ); + + foreach ($tokens as $pos => $tok) { + if (is_string($tok)) { + $token = new Token( + $lastToken->getLine(), + $this->map[$tok], + $tok + ); + $result->addToken($token); + $lastToken = $token; + continue; + } + + $line = $tok[2]; + $values = preg_split('/\R+/Uu', $tok[1]); + + foreach ($values as $v) { + $token = new Token( + $line, + token_name($tok[0]), + $v + ); + $result->addToken($token); + $line++; + $lastToken = $token; + } + } + + return $result; + } + +} +Tokenizer + +Copyright (c) 2017 Arne Blankerts and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +ensureValidUri($value); + $this->value = $value; + } + + public function asString(): string { + return $this->value; + } + + private function ensureValidUri($value) { + if (strpos($value, ':') === false) { + throw new NamespaceUriException( + sprintf("Namespace URI '%s' must contain at least one colon", $value) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + * A context containing previously processed arrays and objects + * when recursively processing a value. + */ +final class Context +{ + /** + * @var array[] + */ + private $arrays; + + /** + * @var \SplObjectStorage + */ + private $objects; + + /** + * Initialises the context + */ + public function __construct() + { + $this->arrays = array(); + $this->objects = new \SplObjectStorage; + } + + /** + * Adds a value to the context. + * + * @param array|object $value The value to add. + * + * @return int|string The ID of the stored value, either as a string or integer. + * + * @throws InvalidArgumentException Thrown if $value is not an array or object + */ + public function add(&$value) + { + if (is_array($value)) { + return $this->addArray($value); + } elseif (is_object($value)) { + return $this->addObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * Checks if the given value exists within the context. + * + * @param array|object $value The value to check. + * + * @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored. + * + * @throws InvalidArgumentException Thrown if $value is not an array or object + */ + public function contains(&$value) + { + if (is_array($value)) { + return $this->containsArray($value); + } elseif (is_object($value)) { + return $this->containsObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * @param array $array + * + * @return bool|int + */ + private function addArray(array &$array) + { + $key = $this->containsArray($array); + + if ($key !== false) { + return $key; + } + + $key = count($this->arrays); + $this->arrays[] = &$array; + + if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) { + $array[] = $key; + $array[] = $this->objects; + } else { /* cover the improbable case too */ + do { + $key = random_int(PHP_INT_MIN, PHP_INT_MAX); + } while (isset($array[$key])); + + $array[$key] = $key; + + do { + $key = random_int(PHP_INT_MIN, PHP_INT_MAX); + } while (isset($array[$key])); + + $array[$key] = $this->objects; + } + + return $key; + } + + /** + * @param object $object + * + * @return string + */ + private function addObject($object) + { + if (!$this->objects->contains($object)) { + $this->objects->attach($object); + } + + return spl_object_hash($object); + } + + /** + * @param array $array + * + * @return int|false + */ + private function containsArray(array &$array) + { + $end = array_slice($array, -2); + + return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false; + } + + /** + * @param object $value + * + * @return string|false + */ + private function containsObject($value) + { + if ($this->objects->contains($value)) { + return spl_object_hash($value); + } + + return false; + } + + public function __destruct() + { + foreach ($this->arrays as &$array) { + if (is_array($array)) { + array_pop($array); + array_pop($array); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +final class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} +Recursion Context + +Copyright (c) 2002-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A simple template engine. + * + * @since Class available since Release 1.0.0 + */ +class Text_Template +{ + /** + * @var string + */ + protected $template = ''; + + /** + * @var string + */ + protected $openDelimiter = '{'; + + /** + * @var string + */ + protected $closeDelimiter = '}'; + + /** + * @var array + */ + protected $values = array(); + + /** + * Constructor. + * + * @param string $file + * @throws InvalidArgumentException + */ + public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}') + { + $this->setFile($file); + $this->openDelimiter = $openDelimiter; + $this->closeDelimiter = $closeDelimiter; + } + + /** + * Sets the template file. + * + * @param string $file + * @throws InvalidArgumentException + */ + public function setFile($file) + { + $distFile = $file . '.dist'; + + if (file_exists($file)) { + $this->template = file_get_contents($file); + } + + else if (file_exists($distFile)) { + $this->template = file_get_contents($distFile); + } + + else { + throw new InvalidArgumentException( + 'Template file could not be loaded.' + ); + } + } + + /** + * Sets one or more template variables. + * + * @param array $values + * @param bool $merge + */ + public function setVar(array $values, $merge = TRUE) + { + if (!$merge || empty($this->values)) { + $this->values = $values; + } else { + $this->values = array_merge($this->values, $values); + } + } + + /** + * Renders the template and returns the result. + * + * @return string + */ + public function render() + { + $keys = array(); + + foreach ($this->values as $key => $value) { + $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; + } + + return str_replace($keys, $this->values, $this->template); + } + + /** + * Renders the template and writes the result to a file. + * + * @param string $target + */ + public function renderTo($target) + { + $fp = @fopen($target, 'wt'); + + if ($fp) { + fwrite($fp, $this->render()); + fclose($fp); + } else { + $error = error_get_last(); + + throw new RuntimeException( + sprintf( + 'Could not write to %s: %s', + $target, + substr( + $error['message'], + strpos($error['message'], ':') + 2 + ) + ) + ); + } + } +} + +Text_Template + +Copyright (c) 2009-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +Object Enumerator + +Copyright (c) 2016-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use SebastianBergmann\Version as VersionId; + +/** + * This class defines the current version of PHPUnit. + */ +class Version +{ + private static $pharVersion = "7.5.6"; + + private static $version; + + /** + * Returns the current version of PHPUnit. + */ + public static function id(): string + { + if (self::$pharVersion !== null) { + return self::$pharVersion; + } + + if (self::$version === null) { + $version = new VersionId('7.5.6', \dirname(__DIR__, 2)); + self::$version = $version->getVersion(); + } + + return self::$version; + } + + public static function series(): string + { + if (\strpos(self::id(), '-')) { + $version = \explode('-', self::id())[0]; + } else { + $version = self::id(); + } + + return \implode('.', \array_slice(\explode('.', $version), 0, 2)); + } + + public static function getVersionString(): string + { + return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; + } + + public static function getReleaseChannel(): string + { + if (\strpos(self::$pharVersion, '-') !== false) { + return '-nightly'; + } + + return ''; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\DataProviderTestSuite; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; + +final class TestSuiteSorter +{ + /** + * @var int + */ + public const ORDER_DEFAULT = 0; + + /** + * @var int + */ + public const ORDER_RANDOMIZED = 1; + + /** + * @var int + */ + public const ORDER_REVERSED = 2; + + /** + * @var int + */ + public const ORDER_DEFECTS_FIRST = 3; + + /** + * @var int + */ + public const ORDER_DURATION = 4; + + /** + * List of sorting weights for all test result codes. A higher number gives higher priority. + */ + private const DEFECT_SORT_WEIGHT = [ + BaseTestRunner::STATUS_ERROR => 6, + BaseTestRunner::STATUS_FAILURE => 5, + BaseTestRunner::STATUS_WARNING => 4, + BaseTestRunner::STATUS_INCOMPLETE => 3, + BaseTestRunner::STATUS_RISKY => 2, + BaseTestRunner::STATUS_SKIPPED => 1, + BaseTestRunner::STATUS_UNKNOWN => 0, + ]; + + /** + * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements + */ + private $defectSortOrder = []; + + /** + * @var TestResultCacheInterface + */ + private $cache; + + /** + * @var array array A list of normalized names of tests before reordering + */ + private $originalExecutionOrder = []; + + /** + * @var array array A list of normalized names of tests affected by reordering + */ + private $executionOrder = []; + + public static function getTestSorterUID(Test $test): string + { + if ($test instanceof PhptTestCase) { + return $test->getName(); + } + + if ($test instanceof TestCase) { + $testName = $test->getName(true); + + if (\strpos($testName, '::') === false) { + $testName = \get_class($test) . '::' . $testName; + } + + return $testName; + } + + return $test->getName(); + } + + public function __construct(?TestResultCacheInterface $cache = null) + { + $this->cache = $cache ?? new NullTestResultCache; + } + + /** + * @throws Exception + */ + public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = true): void + { + $allowedOrders = [ + self::ORDER_DEFAULT, + self::ORDER_REVERSED, + self::ORDER_RANDOMIZED, + self::ORDER_DURATION, + ]; + + if (!\in_array($order, $allowedOrders, true)) { + throw new Exception( + '$order must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_REVERSED, or TestSuiteSorter::ORDER_RANDOMIZED, or TestSuiteSorter::ORDER_DURATION' + ); + } + + $allowedOrderDefects = [ + self::ORDER_DEFAULT, + self::ORDER_DEFECTS_FIRST, + ]; + + if (!\in_array($orderDefects, $allowedOrderDefects, true)) { + throw new Exception( + '$orderDefects must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_DEFECTS_FIRST' + ); + } + + if ($isRootTestSuite) { + $this->originalExecutionOrder = $this->calculateTestExecutionOrder($suite); + } + + if ($suite instanceof TestSuite) { + foreach ($suite as $_suite) { + $this->reorderTestsInSuite($_suite, $order, $resolveDependencies, $orderDefects, false); + } + + if ($orderDefects === self::ORDER_DEFECTS_FIRST) { + $this->addSuiteToDefectSortOrder($suite); + } + + $this->sort($suite, $order, $resolveDependencies, $orderDefects); + } + + if ($isRootTestSuite) { + $this->executionOrder = $this->calculateTestExecutionOrder($suite); + } + } + + public function getOriginalExecutionOrder(): array + { + return $this->originalExecutionOrder; + } + + public function getExecutionOrder(): array + { + return $this->executionOrder; + } + + private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects): void + { + if (empty($suite->tests())) { + return; + } + + if ($order === self::ORDER_REVERSED) { + $suite->setTests($this->reverse($suite->tests())); + } elseif ($order === self::ORDER_RANDOMIZED) { + $suite->setTests($this->randomize($suite->tests())); + } elseif ($order === self::ORDER_DURATION && $this->cache !== null) { + $suite->setTests($this->sortByDuration($suite->tests())); + } + + if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) { + $suite->setTests($this->sortDefectsFirst($suite->tests())); + } + + if ($resolveDependencies && !($suite instanceof DataProviderTestSuite) && $this->suiteOnlyContainsTests($suite)) { + $suite->setTests($this->resolveDependencies($suite->tests())); + } + } + + private function addSuiteToDefectSortOrder(TestSuite $suite): void + { + $max = 0; + + foreach ($suite->tests() as $test) { + $testname = self::getTestSorterUID($test); + + if (!isset($this->defectSortOrder[$testname])) { + $this->defectSortOrder[$testname] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($testname)]; + $max = \max($max, $this->defectSortOrder[$testname]); + } + } + + $this->defectSortOrder[$suite->getName()] = $max; + } + + private function suiteOnlyContainsTests(TestSuite $suite): bool + { + return \array_reduce( + $suite->tests(), + function ($carry, $test) { + return $carry && ($test instanceof TestCase || $test instanceof DataProviderTestSuite); + }, + true + ); + } + + private function reverse(array $tests): array + { + return \array_reverse($tests); + } + + private function randomize(array $tests): array + { + \shuffle($tests); + + return $tests; + } + + private function sortDefectsFirst(array $tests): array + { + \usort( + $tests, + function ($left, $right) { + return $this->cmpDefectPriorityAndTime($left, $right); + } + ); + + return $tests; + } + + private function sortByDuration(array $tests): array + { + \usort( + $tests, + function ($left, $right) { + return $this->cmpDuration($left, $right); + } + ); + + return $tests; + } + + /** + * Comparator callback function to sort tests for "reach failure as fast as possible": + * 1. sort tests by defect weight defined in self::DEFECT_SORT_WEIGHT + * 2. when tests are equally defective, sort the fastest to the front + * 3. do not reorder successful tests + */ + private function cmpDefectPriorityAndTime(Test $a, Test $b): int + { + $priorityA = $this->defectSortOrder[self::getTestSorterUID($a)] ?? 0; + $priorityB = $this->defectSortOrder[self::getTestSorterUID($b)] ?? 0; + + if ($priorityB <=> $priorityA) { + // Sort defect weight descending + return $priorityB <=> $priorityA; + } + + if ($priorityA || $priorityB) { + return $this->cmpDuration($a, $b); + } + + // do not change execution order + return 0; + } + + /** + * Compares test duration for sorting tests by duration ascending. + */ + private function cmpDuration(Test $a, Test $b): int + { + return $this->cache->getTime(self::getTestSorterUID($a)) <=> $this->cache->getTime(self::getTestSorterUID($b)); + } + + /** + * Reorder Tests within a TestCase in such a way as to resolve as many dependencies as possible. + * The algorithm will leave the tests in original running order when it can. + * For more details see the documentation for test dependencies. + * + * Short description of algorithm: + * 1. Pick the next Test from remaining tests to be checked for dependencies. + * 2. If the test has no dependencies: mark done, start again from the top + * 3. If the test has dependencies but none left to do: mark done, start again from the top + * 4. When we reach the end add any leftover tests to the end. These will be marked 'skipped' during execution. + * + * @param array $tests + * + * @return array + */ + private function resolveDependencies(array $tests): array + { + $newTestOrder = []; + $i = 0; + + do { + $todoNames = \array_map( + function ($test) { + return self::getTestSorterUID($test); + }, + $tests + ); + + if (!$tests[$i]->hasDependencies() || empty(\array_intersect($this->getNormalizedDependencyNames($tests[$i]), $todoNames))) { + $newTestOrder = \array_merge($newTestOrder, \array_splice($tests, $i, 1)); + $i = 0; + } else { + $i++; + } + } while (!empty($tests) && ($i < \count($tests))); + + return \array_merge($newTestOrder, $tests); + } + + /** + * @param DataProviderTestSuite|TestCase $test + * + * @return array A list of full test names as "TestSuiteClassName::testMethodName" + */ + private function getNormalizedDependencyNames($test): array + { + if ($test instanceof DataProviderTestSuite) { + $testClass = \substr($test->getName(), 0, \strpos($test->getName(), '::')); + } else { + $testClass = \get_class($test); + } + + $names = \array_map( + function ($name) use ($testClass) { + return \strpos($name, '::') === false ? $testClass . '::' . $name : $name; + }, + $test->getDependencies() + ); + + return $names; + } + + private function calculateTestExecutionOrder(Test $suite): array + { + $tests = []; + + if ($suite instanceof TestSuite) { + foreach ($suite->tests() as $test) { + if (!($test instanceof TestSuite)) { + $tests[] = self::getTestSorterUID($test); + } else { + $tests = \array_merge($tests, $this->calculateTestExecutionOrder($test)); + } + } + } + + return $tests; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\TestCase; +use PHPUnit\Util\FileLoader; +use PHPUnit\Util\Filesystem; +use ReflectionClass; + +/** + * The standard test suite loader. + */ +class StandardTestSuiteLoader implements TestSuiteLoader +{ + /** + * @throws Exception + * @throws \PHPUnit\Framework\Exception + */ + public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass + { + $suiteClassName = \str_replace('.php', '', $suiteClassName); + + if (empty($suiteClassFile)) { + $suiteClassFile = Filesystem::classNameToFilename( + $suiteClassName + ); + } + + if (!\class_exists($suiteClassName, false)) { + $loadedClasses = \get_declared_classes(); + + $filename = FileLoader::checkAndLoad($suiteClassFile); + + $loadedClasses = \array_values( + \array_diff(\get_declared_classes(), $loadedClasses) + ); + } + + if (!\class_exists($suiteClassName, false) && !empty($loadedClasses)) { + $offset = 0 - \strlen($suiteClassName); + + foreach ($loadedClasses as $loadedClass) { + $class = new ReflectionClass($loadedClass); + + if (\substr($loadedClass, $offset) === $suiteClassName && + $class->getFileName() == $filename) { + $suiteClassName = $loadedClass; + + break; + } + } + } + + if (!\class_exists($suiteClassName, false) && !empty($loadedClasses)) { + $testCaseClass = TestCase::class; + + foreach ($loadedClasses as $loadedClass) { + $class = new ReflectionClass($loadedClass); + $classFile = $class->getFileName(); + + if ($class->isSubclassOf($testCaseClass) && !$class->isAbstract()) { + $suiteClassName = $loadedClass; + $testCaseClass = $loadedClass; + + if ($classFile == \realpath($suiteClassFile)) { + break; + } + } + + if ($class->hasMethod('suite')) { + $method = $class->getMethod('suite'); + + if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) { + $suiteClassName = $loadedClass; + + if ($classFile == \realpath($suiteClassFile)) { + break; + } + } + } + } + } + + if (\class_exists($suiteClassName, false)) { + $class = new ReflectionClass($suiteClassName); + + if ($class->getFileName() == \realpath($suiteClassFile)) { + return $class; + } + } + + throw new Exception( + \sprintf( + "Class '%s' could not be found in '%s'.", + $suiteClassName, + $suiteClassFile + ) + ); + } + + public function reload(ReflectionClass $aClass): ReflectionClass + { + return $aClass; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +final class ResultCacheExtension implements AfterSuccessfulTestHook, AfterSkippedTestHook, AfterRiskyTestHook, AfterIncompleteTestHook, AfterTestErrorHook, AfterTestWarningHook, AfterTestFailureHook, AfterLastTestHook +{ + /** + * @var TestResultCacheInterface + */ + private $cache; + + public function __construct(TestResultCache $cache) + { + $this->cache = $cache; + } + + public function flush(): void + { + $this->cache->persist(); + } + + public function executeAfterSuccessfulTest(string $test, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + } + + public function executeAfterIncompleteTest(string $test, string $message, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + $this->cache->setState($testName, BaseTestRunner::STATUS_INCOMPLETE); + } + + public function executeAfterRiskyTest(string $test, string $message, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + $this->cache->setState($testName, BaseTestRunner::STATUS_RISKY); + } + + public function executeAfterSkippedTest(string $test, string $message, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + $this->cache->setState($testName, BaseTestRunner::STATUS_SKIPPED); + } + + public function executeAfterTestError(string $test, string $message, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + $this->cache->setState($testName, BaseTestRunner::STATUS_ERROR); + } + + public function executeAfterTestFailure(string $test, string $message, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + $this->cache->setState($testName, BaseTestRunner::STATUS_FAILURE); + } + + public function executeAfterTestWarning(string $test, string $message, float $time): void + { + $testName = $this->getTestName($test); + + $this->cache->setTime($testName, \round($time, 3)); + $this->cache->setState($testName, BaseTestRunner::STATUS_WARNING); + } + + public function executeAfterLastTest(): void + { + $this->flush(); + } + + /** + * @param string $test A long description format of the current test + * + * @return string The test name without TestSuiteClassName:: and @dataprovider details + */ + private function getTestName(string $test): string + { + $matches = []; + + if (\preg_match('/^(?\S+::\S+)(?:(? with data set (?:#\d+|"[^"]+"))\s\()?/', $test, $matches)) { + $test = $matches['name'] . ($matches['dataname'] ?? ''); + } + + return $test; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface BeforeTestHook extends TestHook +{ + public function executeBeforeTest(string $test): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Util\Test as TestUtil; + +final class TestListenerAdapter implements TestListener +{ + /** + * @var TestHook[] + */ + private $hooks = []; + + /** + * @var bool + */ + private $lastTestWasNotSuccessful; + + public function add(TestHook $hook): void + { + $this->hooks[] = $hook; + } + + public function startTest(Test $test): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof BeforeTestHook) { + $hook->executeBeforeTest(TestUtil::describeAsString($test)); + } + } + + $this->lastTestWasNotSuccessful = false; + } + + public function addError(Test $test, \Throwable $t, float $time): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterTestErrorHook) { + $hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + + $this->lastTestWasNotSuccessful = true; + } + + public function addWarning(Test $test, Warning $e, float $time): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterTestWarningHook) { + $hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time); + } + } + + $this->lastTestWasNotSuccessful = true; + } + + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterTestFailureHook) { + $hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time); + } + } + + $this->lastTestWasNotSuccessful = true; + } + + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterIncompleteTestHook) { + $hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + + $this->lastTestWasNotSuccessful = true; + } + + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterRiskyTestHook) { + $hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + + $this->lastTestWasNotSuccessful = true; + } + + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterSkippedTestHook) { + $hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time); + } + } + + $this->lastTestWasNotSuccessful = true; + } + + public function endTest(Test $test, float $time): void + { + if ($this->lastTestWasNotSuccessful !== true) { + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterSuccessfulTestHook) { + $hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time); + } + } + } + + foreach ($this->hooks as $hook) { + if ($hook instanceof AfterTestHook) { + $hook->executeAfterTest(TestUtil::describeAsString($test), $time); + } + } + } + + public function startTestSuite(TestSuite $suite): void + { + } + + public function endTestSuite(TestSuite $suite): void + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterTestErrorHook extends TestHook +{ + public function executeAfterTestError(string $test, string $message, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterTestFailureHook extends TestHook +{ + public function executeAfterTestFailure(string $test, string $message, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterLastTestHook extends Hook +{ + public function executeAfterLastTest(): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface BeforeFirstTestHook extends Hook +{ + public function executeBeforeFirstTest(): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface Hook +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterRiskyTestHook extends TestHook +{ + public function executeAfterRiskyTest(string $test, string $message, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterSuccessfulTestHook extends TestHook +{ + public function executeAfterSuccessfulTest(string $test, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterTestWarningHook extends TestHook +{ + public function executeAfterTestWarning(string $test, string $message, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterSkippedTestHook extends TestHook +{ + public function executeAfterSkippedTest(string $test, string $message, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterIncompleteTestHook extends TestHook +{ + public function executeAfterIncompleteTest(string $test, string $message, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface TestHook extends Hook +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface AfterTestHook extends Hook +{ + /** + * This hook will fire after any test, regardless of the result. + * + * For more fine grained control, have a look at the other hooks + * that extend PHPUnit\Runner\Hook. + */ + public function executeAfterTest(string $test, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +class Exception extends \RuntimeException implements \PHPUnit\Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use ReflectionClass; + +/** + * An interface to define how a test suite should be loaded. + */ +interface TestSuiteLoader +{ + public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass; + + public function reload(ReflectionClass $aClass): ReflectionClass; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\IncompleteTestError; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Framework\SkippedTestError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestResult; +use PHPUnit\Util\PHP\AbstractPhpProcess; +use SebastianBergmann\Timer\Timer; +use Text_Template; +use Throwable; + +/** + * Runner for PHPT test cases. + */ +class PhptTestCase implements Test, SelfDescribing +{ + /** + * @var string[] + */ + private const SETTINGS = [ + 'allow_url_fopen=1', + 'auto_append_file=', + 'auto_prepend_file=', + 'disable_functions=', + 'display_errors=1', + 'docref_root=', + 'docref_ext=.html', + 'error_append_string=', + 'error_prepend_string=', + 'error_reporting=-1', + 'html_errors=0', + 'log_errors=0', + 'magic_quotes_runtime=0', + 'output_handler=', + 'open_basedir=', + 'output_buffering=Off', + 'report_memleaks=0', + 'report_zend_debug=0', + 'safe_mode=0', + 'xdebug.default_enable=0', + ]; + + /** + * @var string + */ + private $filename; + + /** + * @var AbstractPhpProcess + */ + private $phpUtil; + + /** + * @var string + */ + private $output = ''; + + /** + * Constructs a test case with the given filename. + * + * @throws Exception + */ + public function __construct(string $filename, AbstractPhpProcess $phpUtil = null) + { + if (!\is_file($filename)) { + throw new Exception( + \sprintf( + 'File "%s" does not exist.', + $filename + ) + ); + } + + $this->filename = $filename; + $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); + } + + /** + * Counts the number of test cases executed by run(TestResult result). + */ + public function count(): int + { + return 1; + } + + /** + * Runs a test and collects its result in a TestResult instance. + * + * @throws Exception + * @throws \ReflectionException + * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException + * @throws \SebastianBergmann\CodeCoverage\RuntimeException + * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function run(TestResult $result = null): TestResult + { + if ($result === null) { + $result = new TestResult; + } + + try { + $sections = $this->parse(); + } catch (Exception $e) { + $result->startTest($this); + $result->addFailure($this, new SkippedTestError($e->getMessage()), 0); + $result->endTest($this, 0); + + return $result; + } + + $code = $this->render($sections['FILE']); + $xfail = false; + $settings = $this->parseIniSection(self::SETTINGS); + + $result->startTest($this); + + if (isset($sections['INI'])) { + $settings = $this->parseIniSection($sections['INI'], $settings); + } + + if (isset($sections['ENV'])) { + $env = $this->parseEnvSection($sections['ENV']); + $this->phpUtil->setEnv($env); + } + + $this->phpUtil->setUseStderrRedirection(true); + + if ($result->enforcesTimeLimit()) { + $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); + } + + $skip = $this->runSkip($sections, $result, $settings); + + if ($skip) { + return $result; + } + + if (isset($sections['XFAIL'])) { + $xfail = \trim($sections['XFAIL']); + } + + if (isset($sections['STDIN'])) { + $this->phpUtil->setStdin($sections['STDIN']); + } + + if (isset($sections['ARGS'])) { + $this->phpUtil->setArgs($sections['ARGS']); + } + + if ($result->getCollectCodeCoverageInformation()) { + $this->renderForCoverage($code); + } + + Timer::start(); + + $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); + $time = Timer::stop(); + $this->output = $jobResult['stdout'] ?? ''; + + if ($result->getCollectCodeCoverageInformation() && ($coverage = $this->cleanupForCoverage())) { + $result->getCodeCoverage()->append($coverage, $this, true, [], [], true); + } + + try { + $this->assertPhptExpectation($sections, $jobResult['stdout']); + } catch (AssertionFailedError $e) { + $failure = $e; + + if ($xfail !== false) { + $failure = new IncompleteTestError($xfail, 0, $e); + } + $result->addFailure($this, $failure, $time); + } catch (Throwable $t) { + $result->addError($this, $t, $time); + } + + if ($result->allCompletelyImplemented() && $xfail !== false) { + $result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time); + } + + $this->runClean($sections); + + $result->endTest($this, $time); + + return $result; + } + + /** + * Returns the name of the test case. + */ + public function getName(): string + { + return $this->toString(); + } + + /** + * Returns a string representation of the test case. + */ + public function toString(): string + { + return $this->filename; + } + + public function usesDataProvider(): bool + { + return false; + } + + public function getNumAssertions(): int + { + return 1; + } + + public function getActualOutput(): string + { + return $this->output; + } + + public function hasOutput(): bool + { + return !empty($this->output); + } + + /** + * Parse --INI-- section key value pairs and return as array. + * + * @param array|string + */ + private function parseIniSection($content, $ini = []): array + { + if (\is_string($content)) { + $content = \explode("\n", \trim($content)); + } + + foreach ($content as $setting) { + if (\strpos($setting, '=') === false) { + continue; + } + + $setting = \explode('=', $setting, 2); + $name = \trim($setting[0]); + $value = \trim($setting[1]); + + if ($name === 'extension' || $name === 'zend_extension') { + if (!isset($ini[$name])) { + $ini[$name] = []; + } + + $ini[$name][] = $value; + + continue; + } + + $ini[$name] = $value; + } + + return $ini; + } + + private function parseEnvSection(string $content): array + { + $env = []; + + foreach (\explode("\n", \trim($content)) as $e) { + $e = \explode('=', \trim($e), 2); + + if (!empty($e[0]) && isset($e[1])) { + $env[$e[0]] = $e[1]; + } + } + + return $env; + } + + /** + * @throws Exception + */ + private function assertPhptExpectation(array $sections, string $output): void + { + $assertions = [ + 'EXPECT' => 'assertEquals', + 'EXPECTF' => 'assertStringMatchesFormat', + 'EXPECTREGEX' => 'assertRegExp', + ]; + + $actual = \preg_replace('/\r\n/', "\n", \trim($output)); + + foreach ($assertions as $sectionName => $sectionAssertion) { + if (isset($sections[$sectionName])) { + $sectionContent = \preg_replace('/\r\n/', "\n", \trim($sections[$sectionName])); + $expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; + + if ($expected === null) { + throw new Exception('No PHPT expectation found'); + } + Assert::$sectionAssertion($expected, $actual); + + return; + } + } + + throw new Exception('No PHPT assertion found'); + } + + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function runSkip(array &$sections, TestResult $result, array $settings): bool + { + if (!isset($sections['SKIPIF'])) { + return false; + } + + $skipif = $this->render($sections['SKIPIF']); + $jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings)); + + if (!\strncasecmp('skip', \ltrim($jobResult['stdout']), 4)) { + $message = ''; + + if (\preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $skipMatch)) { + $message = \substr($skipMatch[1], 2); + } + + $result->addFailure($this, new SkippedTestError($message), 0); + $result->endTest($this, 0); + + return true; + } + + return false; + } + + private function runClean(array &$sections): void + { + $this->phpUtil->setStdin(''); + $this->phpUtil->setArgs(''); + + if (isset($sections['CLEAN'])) { + $cleanCode = $this->render($sections['CLEAN']); + + $this->phpUtil->runJob($cleanCode, self::SETTINGS); + } + } + + /** + * @throws Exception + */ + private function parse(): array + { + $sections = []; + $section = ''; + + $unsupportedSections = [ + 'REDIRECTTEST', + 'REQUEST', + 'POST', + 'PUT', + 'POST_RAW', + 'GZIP_POST', + 'DEFLATE_POST', + 'GET', + 'COOKIE', + 'HEADERS', + 'CGI', + 'EXPECTHEADERS', + 'EXTENSIONS', + 'PHPDBG', + ]; + + foreach (\file($this->filename) as $line) { + if (\preg_match('/^--([_A-Z]+)--/', $line, $result)) { + $section = $result[1]; + $sections[$section] = ''; + + continue; + } + + if (empty($section)) { + throw new Exception('Invalid PHPT file: empty section header'); + } + + $sections[$section] .= $line; + } + + if (isset($sections['FILEEOF'])) { + $sections['FILE'] = \rtrim($sections['FILEEOF'], "\r\n"); + unset($sections['FILEEOF']); + } + + $this->parseExternal($sections); + + if (!$this->validate($sections)) { + throw new Exception('Invalid PHPT file'); + } + + foreach ($unsupportedSections as $section) { + if (isset($sections[$section])) { + throw new Exception( + "PHPUnit does not support PHPT $section sections" + ); + } + } + + return $sections; + } + + /** + * @throws Exception + */ + private function parseExternal(array &$sections): void + { + $allowSections = [ + 'FILE', + 'EXPECT', + 'EXPECTF', + 'EXPECTREGEX', + ]; + $testDirectory = \dirname($this->filename) . \DIRECTORY_SEPARATOR; + + foreach ($allowSections as $section) { + if (isset($sections[$section . '_EXTERNAL'])) { + $externalFilename = \trim($sections[$section . '_EXTERNAL']); + + if (!\is_file($testDirectory . $externalFilename) || + !\is_readable($testDirectory . $externalFilename)) { + throw new Exception( + \sprintf( + 'Could not load --%s-- %s for PHPT file', + $section . '_EXTERNAL', + $testDirectory . $externalFilename + ) + ); + } + + $sections[$section] = \file_get_contents($testDirectory . $externalFilename); + + unset($sections[$section . '_EXTERNAL']); + } + } + } + + private function validate(array &$sections): bool + { + $requiredSections = [ + 'FILE', + [ + 'EXPECT', + 'EXPECTF', + 'EXPECTREGEX', + ], + ]; + + foreach ($requiredSections as $section) { + if (\is_array($section)) { + $foundSection = false; + + foreach ($section as $anySection) { + if (isset($sections[$anySection])) { + $foundSection = true; + + break; + } + } + + if (!$foundSection) { + return false; + } + + continue; + } + + if (!isset($sections[$section])) { + return false; + } + } + + return true; + } + + private function render(string $code): string + { + return \str_replace( + [ + '__DIR__', + '__FILE__', + ], + [ + "'" . \dirname($this->filename) . "'", + "'" . $this->filename . "'", + ], + $code + ); + } + + private function getCoverageFiles(): array + { + $baseDir = \dirname(\realpath($this->filename)) . \DIRECTORY_SEPARATOR; + $basename = \basename($this->filename, 'phpt'); + + return [ + 'coverage' => $baseDir . $basename . 'coverage', + 'job' => $baseDir . $basename . 'php', + ]; + } + + private function renderForCoverage(string &$job): void + { + $files = $this->getCoverageFiles(); + + $template = new Text_Template( + __DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl' + ); + + $composerAutoload = '\'\''; + + if (\defined('PHPUNIT_COMPOSER_INSTALL') && !\defined('PHPUNIT_TESTSUITE')) { + $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true); + } + + $phar = '\'\''; + + if (\defined('__PHPUNIT_PHAR__')) { + $phar = \var_export(__PHPUNIT_PHAR__, true); + } + + $globals = ''; + + if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export( + $GLOBALS['__PHPUNIT_BOOTSTRAP'], + true + ) . ";\n"; + } + + $template->setVar( + [ + 'composerAutoload' => $composerAutoload, + 'phar' => $phar, + 'globals' => $globals, + 'job' => $files['job'], + 'coverageFile' => $files['coverage'], + ] + ); + + \file_put_contents($files['job'], $job); + $job = $template->render(); + } + + private function cleanupForCoverage(): array + { + $files = $this->getCoverageFiles(); + $coverage = @\unserialize(\file_get_contents($files['coverage'])); + + if ($coverage === false) { + $coverage = []; + } + + foreach ($files as $file) { + @\unlink($file); + } + + return $coverage; + } + + private function stringifyIni(array $ini): array + { + $settings = []; + + foreach ($ini as $key => $value) { + if (\is_array($value)) { + foreach ($value as $val) { + $settings[] = $key . '=' . $val; + } + + continue; + } + + $settings[] = $key . '=' . $value; + } + + return $settings; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +class IncludeGroupFilterIterator extends GroupFilterIterator +{ + protected function doAccept(string $hash): bool + { + return \in_array($hash, $this->groupTests, true); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\WarningTestCase; +use PHPUnit\Util\RegularExpression; +use RecursiveFilterIterator; +use RecursiveIterator; + +class NameFilterIterator extends RecursiveFilterIterator +{ + /** + * @var string + */ + protected $filter; + + /** + * @var int + */ + protected $filterMin; + + /** + * @var int + */ + protected $filterMax; + + /** + * @throws \Exception + */ + public function __construct(RecursiveIterator $iterator, string $filter) + { + parent::__construct($iterator); + + $this->setFilter($filter); + } + + public function accept(): bool + { + $test = $this->getInnerIterator()->current(); + + if ($test instanceof TestSuite) { + return true; + } + + $tmp = \PHPUnit\Util\Test::describe($test); + + if ($test instanceof WarningTestCase) { + $name = $test->getMessage(); + } else { + if ($tmp[0] !== '') { + $name = \implode('::', $tmp); + } else { + $name = $tmp[1]; + } + } + + $accepted = @\preg_match($this->filter, $name, $matches); + + if ($accepted && isset($this->filterMax)) { + $set = \end($matches); + $accepted = $set >= $this->filterMin && $set <= $this->filterMax; + } + + return (bool) $accepted; + } + + /** + * @throws \Exception + */ + protected function setFilter(string $filter): void + { + if (RegularExpression::safeMatch($filter, '') === false) { + // Handles: + // * testAssertEqualsSucceeds#4 + // * testAssertEqualsSucceeds#4-8 + if (\preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) { + if (isset($matches[3]) && $matches[2] < $matches[3]) { + $filter = \sprintf( + '%s.*with data set #(\d+)$', + $matches[1] + ); + + $this->filterMin = $matches[2]; + $this->filterMax = $matches[3]; + } else { + $filter = \sprintf( + '%s.*with data set #%s$', + $matches[1], + $matches[2] + ); + } + } // Handles: + // * testDetermineJsonError@JSON_ERROR_NONE + // * testDetermineJsonError@JSON.* + elseif (\preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { + $filter = \sprintf( + '%s.*with data set "%s"$', + $matches[1], + $matches[2] + ); + } + + // Escape delimiters in regular expression. Do NOT use preg_quote, + // to keep magic characters. + $filter = \sprintf('/%s/i', \str_replace( + '/', + '\\/', + $filter + )); + } + + $this->filter = $filter; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use FilterIterator; +use InvalidArgumentException; +use Iterator; +use PHPUnit\Framework\TestSuite; +use ReflectionClass; + +class Factory +{ + /** + * @var array + */ + private $filters = []; + + /** + * @throws InvalidArgumentException + */ + public function addFilter(ReflectionClass $filter, $args): void + { + if (!$filter->isSubclassOf(\RecursiveFilterIterator::class)) { + throw new InvalidArgumentException( + \sprintf( + 'Class "%s" does not extend RecursiveFilterIterator', + $filter->name + ) + ); + } + + $this->filters[] = [$filter, $args]; + } + + public function factory(Iterator $iterator, TestSuite $suite): FilterIterator + { + foreach ($this->filters as $filter) { + [$class, $args] = $filter; + $iterator = $class->newInstance($iterator, $args, $suite); + } + + return $iterator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +use PHPUnit\Framework\TestSuite; +use RecursiveFilterIterator; +use RecursiveIterator; + +abstract class GroupFilterIterator extends RecursiveFilterIterator +{ + /** + * @var string[] + */ + protected $groupTests = []; + + public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite) + { + parent::__construct($iterator); + + foreach ($suite->getGroupDetails() as $group => $tests) { + if (\in_array((string) $group, $groups, true)) { + $testHashes = \array_map( + 'spl_object_hash', + $tests + ); + + $this->groupTests = \array_merge($this->groupTests, $testHashes); + } + } + } + + public function accept(): bool + { + $test = $this->getInnerIterator()->current(); + + if ($test instanceof TestSuite) { + return true; + } + + return $this->doAccept(\spl_object_hash($test)); + } + + abstract protected function doAccept(string $hash); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner\Filter; + +class ExcludeGroupFilterIterator extends GroupFilterIterator +{ + protected function doAccept(string $hash): bool + { + return !\in_array($hash, $this->groupTests, true); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestSuite; +use ReflectionClass; +use ReflectionException; +use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; + +/** + * Base class for all test runners. + */ +abstract class BaseTestRunner +{ + public const STATUS_UNKNOWN = -1; + + public const STATUS_PASSED = 0; + + public const STATUS_SKIPPED = 1; + + public const STATUS_INCOMPLETE = 2; + + public const STATUS_FAILURE = 3; + + public const STATUS_ERROR = 4; + + public const STATUS_RISKY = 5; + + public const STATUS_WARNING = 6; + + public const SUITE_METHODNAME = 'suite'; + + /** + * Returns the loader to be used. + */ + public function getLoader(): TestSuiteLoader + { + return new StandardTestSuiteLoader; + } + + /** + * Returns the Test corresponding to the given suite. + * This is a template method, subclasses override + * the runFailed() and clearStatus() methods. + * + * @param array|string $suffixes + * + * @throws Exception + */ + public function getTest(string $suiteClassName, string $suiteClassFile = '', $suffixes = ''): ?Test + { + if (\is_dir($suiteClassName) && + !\is_file($suiteClassName . '.php') && empty($suiteClassFile)) { + $facade = new FileIteratorFacade; + $files = $facade->getFilesAsArray( + $suiteClassName, + $suffixes + ); + + $suite = new TestSuite($suiteClassName); + $suite->addTestFiles($files); + + return $suite; + } + + try { + $testClass = $this->loadSuiteClass( + $suiteClassName, + $suiteClassFile + ); + } catch (Exception $e) { + $this->runFailed($e->getMessage()); + + return null; + } + + try { + $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); + + if (!$suiteMethod->isStatic()) { + $this->runFailed( + 'suite() method must be static.' + ); + + return null; + } + + try { + $test = $suiteMethod->invoke(null, $testClass->getName()); + } catch (ReflectionException $e) { + $this->runFailed( + \sprintf( + "Failed to invoke suite() method.\n%s", + $e->getMessage() + ) + ); + + return null; + } + } catch (ReflectionException $e) { + try { + $test = new TestSuite($testClass); + } catch (Exception $e) { + $test = new TestSuite; + $test->setName($suiteClassName); + } + } + + $this->clearStatus(); + + return $test; + } + + /** + * Returns the loaded ReflectionClass for a suite name. + */ + protected function loadSuiteClass(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass + { + $loader = $this->getLoader(); + + return $loader->load($suiteClassName, $suiteClassFile); + } + + /** + * Clears the status message. + */ + protected function clearStatus(): void + { + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + */ + abstract protected function runFailed(string $message); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit; + +/** + * Marker interface for PHPUnit exceptions. + */ +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use PharIo\Manifest\ApplicationName; +use PharIo\Manifest\Exception as ManifestException; +use PharIo\Manifest\ManifestLoader; +use PharIo\Version\Version as PharIoVersion; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Runner\StandardTestSuiteLoader; +use PHPUnit\Runner\TestSuiteLoader; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\Runner\Version; +use PHPUnit\Util\Configuration; +use PHPUnit\Util\ConfigurationGenerator; +use PHPUnit\Util\FileLoader; +use PHPUnit\Util\Filesystem; +use PHPUnit\Util\Getopt; +use PHPUnit\Util\Log\TeamCity; +use PHPUnit\Util\Printer; +use PHPUnit\Util\TestDox\CliTestDoxPrinter; +use PHPUnit\Util\TextTestListRenderer; +use PHPUnit\Util\XmlTestListRenderer; +use ReflectionClass; +use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; + +use Throwable; + +/** + * A TestRunner for the Command Line Interface (CLI) + * PHP SAPI Module. + */ +class Command +{ + /** + * @var array + */ + protected $arguments = [ + 'listGroups' => false, + 'listSuites' => false, + 'listTests' => false, + 'listTestsXml' => false, + 'loader' => null, + 'useDefaultConfiguration' => true, + 'loadedExtensions' => [], + 'notLoadedExtensions' => [], + ]; + + /** + * @var array + */ + protected $options = []; + + /** + * @var array + */ + protected $longOptions = [ + 'atleast-version=' => null, + 'prepend=' => null, + 'bootstrap=' => null, + 'cache-result' => null, + 'cache-result-file=' => null, + 'check-version' => null, + 'colors==' => null, + 'columns=' => null, + 'configuration=' => null, + 'coverage-clover=' => null, + 'coverage-crap4j=' => null, + 'coverage-html=' => null, + 'coverage-php=' => null, + 'coverage-text==' => null, + 'coverage-xml=' => null, + 'debug' => null, + 'disallow-test-output' => null, + 'disallow-resource-usage' => null, + 'disallow-todo-tests' => null, + 'default-time-limit=' => null, + 'enforce-time-limit' => null, + 'exclude-group=' => null, + 'filter=' => null, + 'generate-configuration' => null, + 'globals-backup' => null, + 'group=' => null, + 'help' => null, + 'resolve-dependencies' => null, + 'ignore-dependencies' => null, + 'include-path=' => null, + 'list-groups' => null, + 'list-suites' => null, + 'list-tests' => null, + 'list-tests-xml=' => null, + 'loader=' => null, + 'log-junit=' => null, + 'log-teamcity=' => null, + 'no-configuration' => null, + 'no-coverage' => null, + 'no-logging' => null, + 'no-extensions' => null, + 'order-by=' => null, + 'printer=' => null, + 'process-isolation' => null, + 'repeat=' => null, + 'dont-report-useless-tests' => null, + 'random-order' => null, + 'random-order-seed=' => null, + 'reverse-order' => null, + 'reverse-list' => null, + 'static-backup' => null, + 'stderr' => null, + 'stop-on-defect' => null, + 'stop-on-error' => null, + 'stop-on-failure' => null, + 'stop-on-warning' => null, + 'stop-on-incomplete' => null, + 'stop-on-risky' => null, + 'stop-on-skipped' => null, + 'fail-on-warning' => null, + 'fail-on-risky' => null, + 'strict-coverage' => null, + 'disable-coverage-ignore' => null, + 'strict-global-state' => null, + 'teamcity' => null, + 'testdox' => null, + 'testdox-group=' => null, + 'testdox-exclude-group=' => null, + 'testdox-html=' => null, + 'testdox-text=' => null, + 'testdox-xml=' => null, + 'test-suffix=' => null, + 'testsuite=' => null, + 'verbose' => null, + 'version' => null, + 'whitelist=' => null, + 'dump-xdebug-filter=' => null, + ]; + + /** + * @var bool + */ + private $versionStringPrinted = false; + + /** + * @throws \RuntimeException + * @throws \PHPUnit\Framework\Exception + * @throws \InvalidArgumentException + */ + public static function main(bool $exit = true): int + { + $command = new static; + + return $command->run($_SERVER['argv'], $exit); + } + + /** + * @throws \RuntimeException + * @throws \ReflectionException + * @throws \InvalidArgumentException + * @throws Exception + */ + public function run(array $argv, bool $exit = true): int + { + $this->handleArguments($argv); + + $runner = $this->createRunner(); + + if ($this->arguments['test'] instanceof Test) { + $suite = $this->arguments['test']; + } else { + $suite = $runner->getTest( + $this->arguments['test'], + $this->arguments['testFile'], + $this->arguments['testSuffixes'] + ); + } + + if ($this->arguments['listGroups']) { + return $this->handleListGroups($suite, $exit); + } + + if ($this->arguments['listSuites']) { + return $this->handleListSuites($exit); + } + + if ($this->arguments['listTests']) { + return $this->handleListTests($suite, $exit); + } + + if ($this->arguments['listTestsXml']) { + return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit); + } + + unset($this->arguments['test'], $this->arguments['testFile']); + + try { + $result = $runner->doRun($suite, $this->arguments, $exit); + } catch (Exception $e) { + print $e->getMessage() . \PHP_EOL; + } + + $return = TestRunner::FAILURE_EXIT; + + if (isset($result) && $result->wasSuccessful()) { + $return = TestRunner::SUCCESS_EXIT; + } elseif (!isset($result) || $result->errorCount() > 0) { + $return = TestRunner::EXCEPTION_EXIT; + } + + if ($exit) { + exit($return); + } + + return $return; + } + + /** + * Create a TestRunner, override in subclasses. + */ + protected function createRunner(): TestRunner + { + return new TestRunner($this->arguments['loader']); + } + + /** + * Handles the command-line arguments. + * + * A child class of PHPUnit\TextUI\Command can hook into the argument + * parsing by adding the switch(es) to the $longOptions array and point to a + * callback method that handles the switch(es) in the child class like this + * + * + * longOptions['my-switch'] = 'myHandler'; + * // my-secondswitch will accept a value - note the equals sign + * $this->longOptions['my-secondswitch='] = 'myOtherHandler'; + * } + * + * // --my-switch -> myHandler() + * protected function myHandler() + * { + * } + * + * // --my-secondswitch foo -> myOtherHandler('foo') + * protected function myOtherHandler ($value) + * { + * } + * + * // You will also need this - the static keyword in the + * // PHPUnit\TextUI\Command will mean that it'll be + * // PHPUnit\TextUI\Command that gets instantiated, + * // not MyCommand + * public static function main($exit = true) + * { + * $command = new static; + * + * return $command->run($_SERVER['argv'], $exit); + * } + * + * } + * + * + * @throws Exception + */ + protected function handleArguments(array $argv): void + { + try { + $this->options = Getopt::getopt( + $argv, + 'd:c:hv', + \array_keys($this->longOptions) + ); + } catch (Exception $t) { + $this->exitWithErrorMessage($t->getMessage()); + } + + foreach ($this->options[0] as $option) { + switch ($option[0]) { + case '--colors': + $this->arguments['colors'] = $option[1] ?: ResultPrinter::COLOR_AUTO; + + break; + + case '--bootstrap': + $this->arguments['bootstrap'] = $option[1]; + + break; + + case '--cache-result': + $this->arguments['cacheResult'] = true; + + break; + + case '--cache-result-file': + $this->arguments['cacheResultFile'] = $option[1]; + + break; + + case '--columns': + if (\is_numeric($option[1])) { + $this->arguments['columns'] = (int) $option[1]; + } elseif ($option[1] === 'max') { + $this->arguments['columns'] = 'max'; + } + + break; + + case 'c': + case '--configuration': + $this->arguments['configuration'] = $option[1]; + + break; + + case '--coverage-clover': + $this->arguments['coverageClover'] = $option[1]; + + break; + + case '--coverage-crap4j': + $this->arguments['coverageCrap4J'] = $option[1]; + + break; + + case '--coverage-html': + $this->arguments['coverageHtml'] = $option[1]; + + break; + + case '--coverage-php': + $this->arguments['coveragePHP'] = $option[1]; + + break; + + case '--coverage-text': + if ($option[1] === null) { + $option[1] = 'php://stdout'; + } + + $this->arguments['coverageText'] = $option[1]; + $this->arguments['coverageTextShowUncoveredFiles'] = false; + $this->arguments['coverageTextShowOnlySummary'] = false; + + break; + + case '--coverage-xml': + $this->arguments['coverageXml'] = $option[1]; + + break; + + case 'd': + $ini = \explode('=', $option[1]); + + if (isset($ini[0])) { + if (isset($ini[1])) { + \ini_set($ini[0], $ini[1]); + } else { + \ini_set($ini[0], true); + } + } + + break; + + case '--debug': + $this->arguments['debug'] = true; + + break; + + case 'h': + case '--help': + $this->showHelp(); + exit(TestRunner::SUCCESS_EXIT); + + break; + + case '--filter': + $this->arguments['filter'] = $option[1]; + + break; + + case '--testsuite': + $this->arguments['testsuite'] = $option[1]; + + break; + + case '--generate-configuration': + $this->printVersionString(); + + print 'Generating phpunit.xml in ' . \getcwd() . \PHP_EOL . \PHP_EOL; + + print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; + $bootstrapScript = \trim(\fgets(\STDIN)); + + print 'Tests directory (relative to path shown above; default: tests): '; + $testsDirectory = \trim(\fgets(\STDIN)); + + print 'Source directory (relative to path shown above; default: src): '; + $src = \trim(\fgets(\STDIN)); + + if ($bootstrapScript === '') { + $bootstrapScript = 'vendor/autoload.php'; + } + + if ($testsDirectory === '') { + $testsDirectory = 'tests'; + } + + if ($src === '') { + $src = 'src'; + } + + $generator = new ConfigurationGenerator; + + \file_put_contents( + 'phpunit.xml', + $generator->generateDefaultConfiguration( + Version::series(), + $bootstrapScript, + $testsDirectory, + $src + ) + ); + + print \PHP_EOL . 'Generated phpunit.xml in ' . \getcwd() . \PHP_EOL; + + exit(TestRunner::SUCCESS_EXIT); + + break; + + case '--group': + $this->arguments['groups'] = \explode(',', $option[1]); + + break; + + case '--exclude-group': + $this->arguments['excludeGroups'] = \explode( + ',', + $option[1] + ); + + break; + + case '--test-suffix': + $this->arguments['testSuffixes'] = \explode( + ',', + $option[1] + ); + + break; + + case '--include-path': + $includePath = $option[1]; + + break; + + case '--list-groups': + $this->arguments['listGroups'] = true; + + break; + + case '--list-suites': + $this->arguments['listSuites'] = true; + + break; + + case '--list-tests': + $this->arguments['listTests'] = true; + + break; + + case '--list-tests-xml': + $this->arguments['listTestsXml'] = $option[1]; + + break; + + case '--printer': + $this->arguments['printer'] = $option[1]; + + break; + + case '--loader': + $this->arguments['loader'] = $option[1]; + + break; + + case '--log-junit': + $this->arguments['junitLogfile'] = $option[1]; + + break; + + case '--log-teamcity': + $this->arguments['teamcityLogfile'] = $option[1]; + + break; + + case '--order-by': + $this->handleOrderByOption($option[1]); + + break; + + case '--process-isolation': + $this->arguments['processIsolation'] = true; + + break; + + case '--repeat': + $this->arguments['repeat'] = (int) $option[1]; + + break; + + case '--stderr': + $this->arguments['stderr'] = true; + + break; + + case '--stop-on-defect': + $this->arguments['stopOnDefect'] = true; + + break; + + case '--stop-on-error': + $this->arguments['stopOnError'] = true; + + break; + + case '--stop-on-failure': + $this->arguments['stopOnFailure'] = true; + + break; + + case '--stop-on-warning': + $this->arguments['stopOnWarning'] = true; + + break; + + case '--stop-on-incomplete': + $this->arguments['stopOnIncomplete'] = true; + + break; + + case '--stop-on-risky': + $this->arguments['stopOnRisky'] = true; + + break; + + case '--stop-on-skipped': + $this->arguments['stopOnSkipped'] = true; + + break; + + case '--fail-on-warning': + $this->arguments['failOnWarning'] = true; + + break; + + case '--fail-on-risky': + $this->arguments['failOnRisky'] = true; + + break; + + case '--teamcity': + $this->arguments['printer'] = TeamCity::class; + + break; + + case '--testdox': + $this->arguments['printer'] = CliTestDoxPrinter::class; + + break; + + case '--testdox-group': + $this->arguments['testdoxGroups'] = \explode( + ',', + $option[1] + ); + + break; + + case '--testdox-exclude-group': + $this->arguments['testdoxExcludeGroups'] = \explode( + ',', + $option[1] + ); + + break; + + case '--testdox-html': + $this->arguments['testdoxHTMLFile'] = $option[1]; + + break; + + case '--testdox-text': + $this->arguments['testdoxTextFile'] = $option[1]; + + break; + + case '--testdox-xml': + $this->arguments['testdoxXMLFile'] = $option[1]; + + break; + + case '--no-configuration': + $this->arguments['useDefaultConfiguration'] = false; + + break; + + case '--no-extensions': + $this->arguments['noExtensions'] = true; + + break; + + case '--no-coverage': + $this->arguments['noCoverage'] = true; + + break; + + case '--no-logging': + $this->arguments['noLogging'] = true; + + break; + + case '--globals-backup': + $this->arguments['backupGlobals'] = true; + + break; + + case '--static-backup': + $this->arguments['backupStaticAttributes'] = true; + + break; + + case 'v': + case '--verbose': + $this->arguments['verbose'] = true; + + break; + + case '--atleast-version': + if (\version_compare(Version::id(), $option[1], '>=')) { + exit(TestRunner::SUCCESS_EXIT); + } + + exit(TestRunner::FAILURE_EXIT); + + break; + + case '--version': + $this->printVersionString(); + exit(TestRunner::SUCCESS_EXIT); + + break; + + case '--dont-report-useless-tests': + $this->arguments['reportUselessTests'] = false; + + break; + + case '--strict-coverage': + $this->arguments['strictCoverage'] = true; + + break; + + case '--disable-coverage-ignore': + $this->arguments['disableCodeCoverageIgnore'] = true; + + break; + + case '--strict-global-state': + $this->arguments['beStrictAboutChangesToGlobalState'] = true; + + break; + + case '--disallow-test-output': + $this->arguments['disallowTestOutput'] = true; + + break; + + case '--disallow-resource-usage': + $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true; + + break; + + case '--default-time-limit': + $this->arguments['defaultTimeLimit'] = (int) $option[1]; + + break; + + case '--enforce-time-limit': + $this->arguments['enforceTimeLimit'] = true; + + break; + + case '--disallow-todo-tests': + $this->arguments['disallowTodoAnnotatedTests'] = true; + + break; + + case '--reverse-list': + $this->arguments['reverseList'] = true; + + break; + + case '--check-version': + $this->handleVersionCheck(); + + break; + + case '--whitelist': + $this->arguments['whitelist'] = $option[1]; + + break; + + case '--random-order': + $this->handleOrderByOption('random'); + + break; + + case '--random-order-seed': + $this->arguments['randomOrderSeed'] = (int) $option[1]; + + break; + + case '--resolve-dependencies': + $this->handleOrderByOption('depends'); + + break; + + case '--ignore-dependencies': + $this->arguments['resolveDependencies'] = false; + + break; + + case '--reverse-order': + $this->handleOrderByOption('reverse'); + + break; + + case '--dump-xdebug-filter': + $this->arguments['xdebugFilterFile'] = $option[1]; + + break; + + default: + $optionName = \str_replace('--', '', $option[0]); + + $handler = null; + + if (isset($this->longOptions[$optionName])) { + $handler = $this->longOptions[$optionName]; + } elseif (isset($this->longOptions[$optionName . '='])) { + $handler = $this->longOptions[$optionName . '=']; + } + + if (isset($handler) && \is_callable([$this, $handler])) { + $this->$handler($option[1]); + } + } + } + + $this->handleCustomTestSuite(); + + if (!isset($this->arguments['test'])) { + if (isset($this->options[1][0])) { + $this->arguments['test'] = $this->options[1][0]; + } + + if (isset($this->options[1][1])) { + $this->arguments['testFile'] = \realpath($this->options[1][1]); + } else { + $this->arguments['testFile'] = ''; + } + + if (isset($this->arguments['test']) && + \is_file($this->arguments['test']) && + \substr($this->arguments['test'], -5, 5) != '.phpt') { + $this->arguments['testFile'] = \realpath($this->arguments['test']); + $this->arguments['test'] = \substr($this->arguments['test'], 0, \strrpos($this->arguments['test'], '.')); + } + } + + if (!isset($this->arguments['testSuffixes'])) { + $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; + } + + if (isset($includePath)) { + \ini_set( + 'include_path', + $includePath . \PATH_SEPARATOR . \ini_get('include_path') + ); + } + + if ($this->arguments['loader'] !== null) { + $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); + } + + if (isset($this->arguments['configuration']) && + \is_dir($this->arguments['configuration'])) { + $configurationFile = $this->arguments['configuration'] . '/phpunit.xml'; + + if (\file_exists($configurationFile)) { + $this->arguments['configuration'] = \realpath( + $configurationFile + ); + } elseif (\file_exists($configurationFile . '.dist')) { + $this->arguments['configuration'] = \realpath( + $configurationFile . '.dist' + ); + } + } elseif (!isset($this->arguments['configuration']) && + $this->arguments['useDefaultConfiguration']) { + if (\file_exists('phpunit.xml')) { + $this->arguments['configuration'] = \realpath('phpunit.xml'); + } elseif (\file_exists('phpunit.xml.dist')) { + $this->arguments['configuration'] = \realpath( + 'phpunit.xml.dist' + ); + } + } + + if (isset($this->arguments['configuration'])) { + try { + $configuration = Configuration::getInstance( + $this->arguments['configuration'] + ); + } catch (Throwable $t) { + print $t->getMessage() . \PHP_EOL; + exit(TestRunner::FAILURE_EXIT); + } + + $phpunitConfiguration = $configuration->getPHPUnitConfiguration(); + + $configuration->handlePHPConfiguration(); + + /* + * Issue #1216 + */ + if (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } elseif (isset($phpunitConfiguration['bootstrap'])) { + $this->handleBootstrap($phpunitConfiguration['bootstrap']); + } + + /* + * Issue #657 + */ + if (isset($phpunitConfiguration['stderr']) && !isset($this->arguments['stderr'])) { + $this->arguments['stderr'] = $phpunitConfiguration['stderr']; + } + + if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && \extension_loaded('phar')) { + $this->handleExtensions($phpunitConfiguration['extensionsDirectory']); + } + + if (isset($phpunitConfiguration['columns']) && !isset($this->arguments['columns'])) { + $this->arguments['columns'] = $phpunitConfiguration['columns']; + } + + if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) { + if (isset($phpunitConfiguration['printerFile'])) { + $file = $phpunitConfiguration['printerFile']; + } else { + $file = ''; + } + + $this->arguments['printer'] = $this->handlePrinter( + $phpunitConfiguration['printerClass'], + $file + ); + } + + if (isset($phpunitConfiguration['testSuiteLoaderClass'])) { + if (isset($phpunitConfiguration['testSuiteLoaderFile'])) { + $file = $phpunitConfiguration['testSuiteLoaderFile']; + } else { + $file = ''; + } + + $this->arguments['loader'] = $this->handleLoader( + $phpunitConfiguration['testSuiteLoaderClass'], + $file + ); + } + + if (!isset($this->arguments['testsuite']) && isset($phpunitConfiguration['defaultTestSuite'])) { + $this->arguments['testsuite'] = $phpunitConfiguration['defaultTestSuite']; + } + + if (!isset($this->arguments['test'])) { + $testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? ''); + + if ($testSuite !== null) { + $this->arguments['test'] = $testSuite; + } + } + } elseif (isset($this->arguments['bootstrap'])) { + $this->handleBootstrap($this->arguments['bootstrap']); + } + + if (isset($this->arguments['printer']) && + \is_string($this->arguments['printer'])) { + $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); + } + + if (isset($this->arguments['test']) && \is_string($this->arguments['test']) && \substr($this->arguments['test'], -5, 5) == '.phpt') { + $test = new PhptTestCase($this->arguments['test']); + + $this->arguments['test'] = new TestSuite; + $this->arguments['test']->addTest($test); + } + + if (!isset($this->arguments['test'])) { + $this->showHelp(); + exit(TestRunner::EXCEPTION_EXIT); + } + } + + /** + * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. + */ + protected function handleLoader(string $loaderClass, string $loaderFile = ''): ?TestSuiteLoader + { + if (!\class_exists($loaderClass, false)) { + if ($loaderFile == '') { + $loaderFile = Filesystem::classNameToFilename( + $loaderClass + ); + } + + $loaderFile = \stream_resolve_include_path($loaderFile); + + if ($loaderFile) { + require $loaderFile; + } + } + + if (\class_exists($loaderClass, false)) { + $class = new ReflectionClass($loaderClass); + + if ($class->implementsInterface(TestSuiteLoader::class) && + $class->isInstantiable()) { + return $class->newInstance(); + } + } + + if ($loaderClass == StandardTestSuiteLoader::class) { + return null; + } + + $this->exitWithErrorMessage( + \sprintf( + 'Could not use "%s" as loader.', + $loaderClass + ) + ); + + return null; + } + + /** + * Handles the loading of the PHPUnit\Util\Printer implementation. + * + * @return null|Printer|string + */ + protected function handlePrinter(string $printerClass, string $printerFile = '') + { + if (!\class_exists($printerClass, false)) { + if ($printerFile == '') { + $printerFile = Filesystem::classNameToFilename( + $printerClass + ); + } + + $printerFile = \stream_resolve_include_path($printerFile); + + if ($printerFile) { + require $printerFile; + } + } + + if (!\class_exists($printerClass)) { + $this->exitWithErrorMessage( + \sprintf( + 'Could not use "%s" as printer: class does not exist', + $printerClass + ) + ); + } + + $class = new ReflectionClass($printerClass); + + if (!$class->implementsInterface(TestListener::class)) { + $this->exitWithErrorMessage( + \sprintf( + 'Could not use "%s" as printer: class does not implement %s', + $printerClass, + TestListener::class + ) + ); + } + + if (!$class->isSubclassOf(Printer::class)) { + $this->exitWithErrorMessage( + \sprintf( + 'Could not use "%s" as printer: class does not extend %s', + $printerClass, + Printer::class + ) + ); + } + + if (!$class->isInstantiable()) { + $this->exitWithErrorMessage( + \sprintf( + 'Could not use "%s" as printer: class cannot be instantiated', + $printerClass + ) + ); + } + + if ($class->isSubclassOf(ResultPrinter::class)) { + return $printerClass; + } + + $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; + + return $class->newInstance($outputStream); + } + + /** + * Loads a bootstrap file. + */ + protected function handleBootstrap(string $filename): void + { + try { + FileLoader::checkAndLoad($filename); + } catch (Exception $e) { + $this->exitWithErrorMessage($e->getMessage()); + } + } + + protected function handleVersionCheck(): void + { + $this->printVersionString(); + + $latestVersion = \file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); + $isOutdated = \version_compare($latestVersion, Version::id(), '>'); + + if ($isOutdated) { + \printf( + 'You are not using the latest version of PHPUnit.' . \PHP_EOL . + 'The latest version is PHPUnit %s.' . \PHP_EOL, + $latestVersion + ); + } else { + print 'You are using the latest version of PHPUnit.' . \PHP_EOL; + } + + exit(TestRunner::SUCCESS_EXIT); + } + + /** + * Show the help message. + */ + protected function showHelp(): void + { + $this->printVersionString(); + + print << + +Code Coverage Options: + + --coverage-clover Generate code coverage report in Clover XML format + --coverage-crap4j Generate code coverage report in Crap4J XML format + --coverage-html Generate code coverage report in HTML format + --coverage-php Export PHP_CodeCoverage object to file + --coverage-text= Generate code coverage report in text format + Default: Standard output + --coverage-xml Generate code coverage report in PHPUnit XML format + --whitelist Whitelist for code coverage analysis + --disable-coverage-ignore Disable annotations for ignoring code coverage + --no-coverage Ignore code coverage configuration + --dump-xdebug-filter Generate script to set Xdebug code coverage filter + +Logging Options: + + --log-junit Log test execution in JUnit XML format to file + --log-teamcity Log test execution in TeamCity format to file + --testdox-html Write agile documentation in HTML format to file + --testdox-text Write agile documentation in Text format to file + --testdox-xml Write agile documentation in XML format to file + --reverse-list Print defects in reverse order + +Test Selection Options: + + --filter Filter which tests to run + --testsuite Filter which testsuite to run + --group ... Only runs tests from the specified group(s) + --exclude-group ... Exclude tests from the specified group(s) + --list-groups List available test groups + --list-suites List available test suites + --list-tests List available tests + --list-tests-xml List available tests in XML format + --test-suffix ... Only search for test in files with specified + suffix(es). Default: Test.php,.phpt + +Test Execution Options: + + --dont-report-useless-tests Do not report tests that do not test anything + --strict-coverage Be strict about @covers annotation usage + --strict-global-state Be strict about changes to global state + --disallow-test-output Be strict about output during tests + --disallow-resource-usage Be strict about resource usage during small tests + --enforce-time-limit Enforce time limit based on test size + --default-time-limit= Timeout in seconds for tests without @small, @medium or @large + --disallow-todo-tests Disallow @todo-annotated tests + + --process-isolation Run each test in a separate PHP process + --globals-backup Backup and restore \$GLOBALS for each test + --static-backup Backup and restore static attributes for each test + + --colors= Use colors in output ("never", "auto" or "always") + --columns Number of columns to use for progress output + --columns max Use maximum number of columns for progress output + --stderr Write to STDERR instead of STDOUT + --stop-on-defect Stop execution upon first not-passed test + --stop-on-error Stop execution upon first error + --stop-on-failure Stop execution upon first error or failure + --stop-on-warning Stop execution upon first warning + --stop-on-risky Stop execution upon first risky test + --stop-on-skipped Stop execution upon first skipped test + --stop-on-incomplete Stop execution upon first incomplete test + --fail-on-warning Treat tests with warnings as failures + --fail-on-risky Treat risky tests as failures + -v|--verbose Output more verbose information + --debug Display debugging information + + --loader TestSuiteLoader implementation to use + --repeat Runs the test(s) repeatedly + --teamcity Report test execution progress in TeamCity format + --testdox Report test execution progress in TestDox format + --testdox-group Only include tests from the specified group(s) + --testdox-exclude-group Exclude tests from the specified group(s) + --printer TestListener implementation to use + + --resolve-dependencies Resolve dependencies between tests + --order-by= Run tests in order: default|reverse|random|defects|depends + --random-order-seed= Use a specific random seed for random order + --cache-result Write run result to cache to enable ordering tests defects-first + +Configuration Options: + + --prepend A PHP script that is included as early as possible + --bootstrap A PHP script that is included before the tests run + -c|--configuration Read configuration from XML file + --no-configuration Ignore default configuration file (phpunit.xml) + --no-logging Ignore logging configuration + --no-extensions Do not load PHPUnit extensions + --include-path Prepend PHP's include_path with given path(s) + -d key[=value] Sets a php.ini value + --generate-configuration Generate configuration file with suggested settings + --cache-result-file= Specify result cache path and filename + +Miscellaneous Options: + + -h|--help Prints this usage information + --version Prints the version and exits + --atleast-version Checks that version is greater than min and exits + --check-version Check whether PHPUnit is the latest version + +EOT; + } + + /** + * Custom callback for test suite discovery. + */ + protected function handleCustomTestSuite(): void + { + } + + private function printVersionString(): void + { + if ($this->versionStringPrinted) { + return; + } + + print Version::getVersionString() . \PHP_EOL . \PHP_EOL; + + $this->versionStringPrinted = true; + } + + private function exitWithErrorMessage(string $message): void + { + $this->printVersionString(); + + print $message . \PHP_EOL; + + exit(TestRunner::FAILURE_EXIT); + } + + private function handleExtensions(string $directory): void + { + $facade = new FileIteratorFacade; + + foreach ($facade->getFilesAsArray($directory, '.phar') as $file) { + if (!\file_exists('phar://' . $file . '/manifest.xml')) { + $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit'; + + continue; + } + + try { + $applicationName = new ApplicationName('phpunit/phpunit'); + $version = new PharIoVersion(Version::series()); + $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); + + if (!$manifest->isExtensionFor($applicationName)) { + $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit'; + + continue; + } + + if (!$manifest->isExtensionFor($applicationName, $version)) { + $this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit'; + + continue; + } + } catch (ManifestException $e) { + $this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage(); + + continue; + } + + require $file; + + $this->arguments['loadedExtensions'][] = $manifest->getName() . ' ' . $manifest->getVersion()->getVersionString(); + } + } + + private function handleListGroups(TestSuite $suite, bool $exit): int + { + $this->printVersionString(); + + print 'Available test group(s):' . \PHP_EOL; + + $groups = $suite->getGroups(); + \sort($groups); + + foreach ($groups as $group) { + \printf( + ' - %s' . \PHP_EOL, + $group + ); + } + + if ($exit) { + exit(TestRunner::SUCCESS_EXIT); + } + + return TestRunner::SUCCESS_EXIT; + } + + private function handleListSuites(bool $exit): int + { + $this->printVersionString(); + + print 'Available test suite(s):' . \PHP_EOL; + + $configuration = Configuration::getInstance( + $this->arguments['configuration'] + ); + + $suiteNames = $configuration->getTestSuiteNames(); + + foreach ($suiteNames as $suiteName) { + \printf( + ' - %s' . \PHP_EOL, + $suiteName + ); + } + + if ($exit) { + exit(TestRunner::SUCCESS_EXIT); + } + + return TestRunner::SUCCESS_EXIT; + } + + private function handleListTests(TestSuite $suite, bool $exit): int + { + $this->printVersionString(); + + $renderer = new TextTestListRenderer; + + print $renderer->render($suite); + + if ($exit) { + exit(TestRunner::SUCCESS_EXIT); + } + + return TestRunner::SUCCESS_EXIT; + } + + private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int + { + $this->printVersionString(); + + $renderer = new XmlTestListRenderer; + + \file_put_contents($target, $renderer->render($suite)); + + \printf( + 'Wrote list of tests that would have been run to %s' . \PHP_EOL, + $target + ); + + if ($exit) { + exit(TestRunner::SUCCESS_EXIT); + } + + return TestRunner::SUCCESS_EXIT; + } + + private function handleOrderByOption(string $value): void + { + foreach (\explode(',', $value) as $order) { + switch ($order) { + case 'default': + $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT; + $this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT; + $this->arguments['resolveDependencies'] = false; + + break; + + case 'reverse': + $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_REVERSED; + + break; + + case 'random': + $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED; + + break; + + case 'defects': + $this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST; + + break; + + case 'depends': + $this->arguments['resolveDependencies'] = true; + + break; + + default: + $this->exitWithErrorMessage("unrecognized --order-by option: $order"); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use PHPUnit\Framework\Error\Deprecated; +use PHPUnit\Framework\Error\Notice; +use PHPUnit\Framework\Error\Warning; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\AfterLastTestHook; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\BeforeFirstTestHook; +use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator; +use PHPUnit\Runner\Filter\Factory; +use PHPUnit\Runner\Filter\IncludeGroupFilterIterator; +use PHPUnit\Runner\Filter\NameFilterIterator; +use PHPUnit\Runner\Hook; +use PHPUnit\Runner\NullTestResultCache; +use PHPUnit\Runner\ResultCacheExtension; +use PHPUnit\Runner\StandardTestSuiteLoader; +use PHPUnit\Runner\TestHook; +use PHPUnit\Runner\TestListenerAdapter; +use PHPUnit\Runner\TestResultCache; +use PHPUnit\Runner\TestSuiteLoader; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\Runner\Version; +use PHPUnit\Util\Configuration; +use PHPUnit\Util\Filesystem; +use PHPUnit\Util\Log\JUnit; +use PHPUnit\Util\Log\TeamCity; +use PHPUnit\Util\Printer; +use PHPUnit\Util\TestDox\CliTestDoxPrinter; +use PHPUnit\Util\TestDox\HtmlResultPrinter; +use PHPUnit\Util\TestDox\TextResultPrinter; +use PHPUnit\Util\TestDox\XmlResultPrinter; +use PHPUnit\Util\XdebugFilterScriptGenerator; +use ReflectionClass; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; +use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; +use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; +use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; +use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; +use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; +use SebastianBergmann\CodeCoverage\Report\Text as TextReport; +use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Environment\Runtime; +use SebastianBergmann\Invoker\Invoker; + +/** + * A TestRunner for the Command Line Interface (CLI) + * PHP SAPI Module. + */ +class TestRunner extends BaseTestRunner +{ + public const SUCCESS_EXIT = 0; + + public const FAILURE_EXIT = 1; + + public const EXCEPTION_EXIT = 2; + + /** + * @var bool + */ + protected static $versionStringPrinted = false; + + /** + * @var CodeCoverageFilter + */ + protected $codeCoverageFilter; + + /** + * @var TestSuiteLoader + */ + protected $loader; + + /** + * @var ResultPrinter + */ + protected $printer; + + /** + * @var Runtime + */ + private $runtime; + + /** + * @var bool + */ + private $messagePrinted = false; + + /** + * @var Hook[] + */ + private $extensions = []; + + /** + * @param ReflectionClass|Test $test + * @param bool $exit + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws Exception + * @throws \ReflectionException + */ + public static function run($test, array $arguments = [], $exit = true): TestResult + { + if ($test instanceof ReflectionClass) { + $test = new TestSuite($test); + } + + if ($test instanceof Test) { + $aTestRunner = new self; + + return $aTestRunner->doRun( + $test, + $arguments, + $exit + ); + } + + throw new Exception('No test case or test suite found.'); + } + + public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null) + { + if ($filter === null) { + $filter = new CodeCoverageFilter; + } + + $this->codeCoverageFilter = $filter; + $this->loader = $loader; + $this->runtime = new Runtime; + } + + /** + * @throws \PHPUnit\Runner\Exception + * @throws Exception + * @throws \InvalidArgumentException + * @throws \RuntimeException + * @throws \ReflectionException + */ + public function doRun(Test $suite, array $arguments = [], bool $exit = true): TestResult + { + if (isset($arguments['configuration'])) { + $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; + } + + $this->handleConfiguration($arguments); + + if (\is_int($arguments['columns']) && $arguments['columns'] < 16) { + $arguments['columns'] = 16; + $tooFewColumnsRequested = true; + } + + if (isset($arguments['bootstrap'])) { + $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; + } + + if ($suite instanceof TestCase || $suite instanceof TestSuite) { + if ($arguments['backupGlobals'] === true) { + $suite->setBackupGlobals(true); + } + + if ($arguments['backupStaticAttributes'] === true) { + $suite->setBackupStaticAttributes(true); + } + + if ($arguments['beStrictAboutChangesToGlobalState'] === true) { + $suite->setBeStrictAboutChangesToGlobalState(true); + } + } + + if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { + \mt_srand($arguments['randomOrderSeed']); + } + + if ($arguments['cacheResult']) { + if (isset($arguments['cacheResultFile'])) { + $cache = new TestResultCache($arguments['cacheResultFile']); + } else { + $cache = new TestResultCache; + } + + $this->extensions[] = new ResultCacheExtension($cache); + } + + if ($arguments['executionOrder'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) { + $cache = $cache ?? new NullTestResultCache; + + $cache->load(); + + $sorter = new TestSuiteSorter($cache); + + $sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']); + $originalExecutionOrder = $sorter->getOriginalExecutionOrder(); + + unset($sorter); + } + + if (\is_int($arguments['repeat']) && $arguments['repeat'] > 0) { + $_suite = new TestSuite; + + foreach (\range(1, $arguments['repeat']) as $step) { + $_suite->addTest($suite); + } + + $suite = $_suite; + + unset($_suite); + } + + $result = $this->createTestResult(); + + $listener = new TestListenerAdapter; + $listenerNeeded = false; + + foreach ($this->extensions as $extension) { + if ($extension instanceof TestHook) { + $listener->add($extension); + + $listenerNeeded = true; + } + } + + if ($listenerNeeded) { + $result->addListener($listener); + } + + unset($listener, $listenerNeeded); + + if (!$arguments['convertErrorsToExceptions']) { + $result->convertErrorsToExceptions(false); + } + + if (!$arguments['convertDeprecationsToExceptions']) { + Deprecated::$enabled = false; + } + + if (!$arguments['convertNoticesToExceptions']) { + Notice::$enabled = false; + } + + if (!$arguments['convertWarningsToExceptions']) { + Warning::$enabled = false; + } + + if ($arguments['stopOnError']) { + $result->stopOnError(true); + } + + if ($arguments['stopOnFailure']) { + $result->stopOnFailure(true); + } + + if ($arguments['stopOnWarning']) { + $result->stopOnWarning(true); + } + + if ($arguments['stopOnIncomplete']) { + $result->stopOnIncomplete(true); + } + + if ($arguments['stopOnRisky']) { + $result->stopOnRisky(true); + } + + if ($arguments['stopOnSkipped']) { + $result->stopOnSkipped(true); + } + + if ($arguments['stopOnDefect']) { + $result->stopOnDefect(true); + } + + if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) { + $result->setRegisterMockObjectsFromTestArgumentsRecursively(true); + } + + if ($this->printer === null) { + if (isset($arguments['printer']) && + $arguments['printer'] instanceof Printer) { + $this->printer = $arguments['printer']; + } else { + $printerClass = ResultPrinter::class; + + if (isset($arguments['printer']) && \is_string($arguments['printer']) && \class_exists($arguments['printer'], false)) { + $class = new ReflectionClass($arguments['printer']); + + if ($class->isSubclassOf(ResultPrinter::class)) { + $printerClass = $arguments['printer']; + } + } + + $this->printer = new $printerClass( + (isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null, + $arguments['verbose'], + $arguments['colors'], + $arguments['debug'], + $arguments['columns'], + $arguments['reverseList'] + ); + + if (isset($originalExecutionOrder) && ($this->printer instanceof CliTestDoxPrinter)) { + /* @var CliTestDoxPrinter */ + $this->printer->setOriginalExecutionOrder($originalExecutionOrder); + } + } + } + + $this->printer->write( + Version::getVersionString() . "\n" + ); + + self::$versionStringPrinted = true; + + if ($arguments['verbose']) { + $runtime = $this->runtime->getNameWithVersion(); + + if ($this->runtime->hasXdebug()) { + $runtime .= \sprintf( + ' with Xdebug %s', + \phpversion('xdebug') + ); + } + + $this->writeMessage('Runtime', $runtime); + + if (isset($arguments['configuration'])) { + $this->writeMessage( + 'Configuration', + $arguments['configuration']->getFilename() + ); + } + + foreach ($arguments['loadedExtensions'] as $extension) { + $this->writeMessage( + 'Extension', + $extension + ); + } + + foreach ($arguments['notLoadedExtensions'] as $extension) { + $this->writeMessage( + 'Extension', + $extension + ); + } + } + + if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { + $this->writeMessage( + 'Random seed', + $arguments['randomOrderSeed'] + ); + } + + if (isset($tooFewColumnsRequested)) { + $this->writeMessage('Error', 'Less than 16 columns requested, number of columns set to 16'); + } + + if ($this->runtime->discardsComments()) { + $this->writeMessage('Warning', 'opcache.save_comments=0 set; annotations will not work'); + } + + if (isset($arguments['configuration']) && $arguments['configuration']->hasValidationErrors()) { + $this->write( + "\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n" + ); + + foreach ($arguments['configuration']->getValidationErrors() as $line => $errors) { + $this->write(\sprintf("\n Line %d:\n", $line)); + + foreach ($errors as $msg) { + $this->write(\sprintf(" - %s\n", $msg)); + } + } + $this->write("\n Test results may not be as expected.\n\n"); + } + + foreach ($arguments['listeners'] as $listener) { + $result->addListener($listener); + } + + $result->addListener($this->printer); + + $codeCoverageReports = 0; + + if (!isset($arguments['noLogging'])) { + if (isset($arguments['testdoxHTMLFile'])) { + $result->addListener( + new HtmlResultPrinter( + $arguments['testdoxHTMLFile'], + $arguments['testdoxGroups'], + $arguments['testdoxExcludeGroups'] + ) + ); + } + + if (isset($arguments['testdoxTextFile'])) { + $result->addListener( + new TextResultPrinter( + $arguments['testdoxTextFile'], + $arguments['testdoxGroups'], + $arguments['testdoxExcludeGroups'] + ) + ); + } + + if (isset($arguments['testdoxXMLFile'])) { + $result->addListener( + new XmlResultPrinter( + $arguments['testdoxXMLFile'] + ) + ); + } + + if (isset($arguments['teamcityLogfile'])) { + $result->addListener( + new TeamCity($arguments['teamcityLogfile']) + ); + } + + if (isset($arguments['junitLogfile'])) { + $result->addListener( + new JUnit( + $arguments['junitLogfile'], + $arguments['reportUselessTests'] + ) + ); + } + + if (isset($arguments['coverageClover'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageCrap4J'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageHtml'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coveragePHP'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageText'])) { + $codeCoverageReports++; + } + + if (isset($arguments['coverageXml'])) { + $codeCoverageReports++; + } + } + + if (isset($arguments['noCoverage'])) { + $codeCoverageReports = 0; + } + + if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) { + $this->writeMessage('Error', 'No code coverage driver is available'); + + $codeCoverageReports = 0; + } + + if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { + $whitelistFromConfigurationFile = false; + $whitelistFromOption = false; + + if (isset($arguments['whitelist'])) { + $this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']); + + $whitelistFromOption = true; + } + + if (isset($arguments['configuration'])) { + $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); + + if (!empty($filterConfiguration['whitelist'])) { + $whitelistFromConfigurationFile = true; + } + + if (!empty($filterConfiguration['whitelist'])) { + foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) { + $this->codeCoverageFilter->addDirectoryToWhitelist( + $dir['path'], + $dir['suffix'], + $dir['prefix'] + ); + } + + foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { + $this->codeCoverageFilter->addFileToWhitelist($file); + } + + foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { + $this->codeCoverageFilter->removeDirectoryFromWhitelist( + $dir['path'], + $dir['suffix'], + $dir['prefix'] + ); + } + + foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { + $this->codeCoverageFilter->removeFileFromWhitelist($file); + } + } + } + } + + if ($codeCoverageReports > 0) { + $codeCoverage = new CodeCoverage( + null, + $this->codeCoverageFilter + ); + + $codeCoverage->setUnintentionallyCoveredSubclassesWhitelist( + [Comparator::class] + ); + + $codeCoverage->setCheckForUnintentionallyCoveredCode( + $arguments['strictCoverage'] + ); + + $codeCoverage->setCheckForMissingCoversAnnotation( + $arguments['strictCoverage'] + ); + + if (isset($arguments['forceCoversAnnotation'])) { + $codeCoverage->setForceCoversAnnotation( + $arguments['forceCoversAnnotation'] + ); + } + + if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { + $codeCoverage->setIgnoreDeprecatedCode( + $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] + ); + } + + if (isset($arguments['disableCodeCoverageIgnore'])) { + $codeCoverage->setDisableIgnoredLines(true); + } + + if (!empty($filterConfiguration['whitelist'])) { + $codeCoverage->setAddUncoveredFilesFromWhitelist( + $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist'] + ); + + $codeCoverage->setProcessUncoveredFilesFromWhitelist( + $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist'] + ); + } + + if (!$this->codeCoverageFilter->hasWhitelist()) { + if (!$whitelistFromConfigurationFile && !$whitelistFromOption) { + $this->writeMessage('Error', 'No whitelist is configured, no code coverage will be generated.'); + } else { + $this->writeMessage('Error', 'Incorrect whitelist config, no code coverage will be generated.'); + } + + $codeCoverageReports = 0; + + unset($codeCoverage); + } + } + + if (isset($arguments['xdebugFilterFile'], $filterConfiguration)) { + $this->write("\n"); + + $script = (new XdebugFilterScriptGenerator)->generate($filterConfiguration['whitelist']); + + if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !Filesystem::createDirectory(\dirname($arguments['xdebugFilterFile']))) { + $this->write(\sprintf('Cannot write Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile'])); + + exit(self::EXCEPTION_EXIT); + } + + \file_put_contents($arguments['xdebugFilterFile'], $script); + + $this->write(\sprintf('Wrote Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile'])); + + exit(self::SUCCESS_EXIT); + } + + $this->printer->write("\n"); + + if (isset($codeCoverage)) { + $result->setCodeCoverage($codeCoverage); + + if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) { + $codeCoverage->setCacheTokens($arguments['cacheTokens']); + } + } + + $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); + $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); + $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); + $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); + + if ($arguments['enforceTimeLimit'] === true) { + if (!\class_exists(Invoker::class)) { + $this->writeMessage('Error', 'Package phpunit/php-invoker is required for enforcing time limits'); + } + + if (!\extension_loaded('pcntl') || \strpos(\ini_get('disable_functions'), 'pcntl') !== false) { + $this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits'); + } + } + $result->enforceTimeLimit($arguments['enforceTimeLimit']); + $result->setDefaultTimeLimit($arguments['defaultTimeLimit']); + $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']); + $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']); + $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']); + + if ($suite instanceof TestSuite) { + $this->processSuiteFilters($suite, $arguments); + $suite->setRunTestInSeparateProcess($arguments['processIsolation']); + } + + foreach ($this->extensions as $extension) { + if ($extension instanceof BeforeFirstTestHook) { + $extension->executeBeforeFirstTest(); + } + } + + $suite->run($result); + + foreach ($this->extensions as $extension) { + if ($extension instanceof AfterLastTestHook) { + $extension->executeAfterLastTest(); + } + } + + $result->flushListeners(); + + if ($this->printer instanceof ResultPrinter) { + $this->printer->printResult($result); + } + + if (isset($codeCoverage)) { + if (isset($arguments['coverageClover'])) { + $this->printer->write( + "\nGenerating code coverage report in Clover XML format ..." + ); + + try { + $writer = new CloverReport; + $writer->process($codeCoverage, $arguments['coverageClover']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (CodeCoverageException $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coverageCrap4J'])) { + $this->printer->write( + "\nGenerating Crap4J report XML file ..." + ); + + try { + $writer = new Crap4jReport($arguments['crap4jThreshold']); + $writer->process($codeCoverage, $arguments['coverageCrap4J']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (CodeCoverageException $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coverageHtml'])) { + $this->printer->write( + "\nGenerating code coverage report in HTML format ..." + ); + + try { + $writer = new HtmlReport( + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + \sprintf( + ' and PHPUnit %s', + Version::id() + ) + ); + + $writer->process($codeCoverage, $arguments['coverageHtml']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (CodeCoverageException $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coveragePHP'])) { + $this->printer->write( + "\nGenerating code coverage report in PHP format ..." + ); + + try { + $writer = new PhpReport; + $writer->process($codeCoverage, $arguments['coveragePHP']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (CodeCoverageException $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + + if (isset($arguments['coverageText'])) { + if ($arguments['coverageText'] == 'php://stdout') { + $outputStream = $this->printer; + $colors = $arguments['colors'] && $arguments['colors'] != ResultPrinter::COLOR_NEVER; + } else { + $outputStream = new Printer($arguments['coverageText']); + $colors = false; + } + + $processor = new TextReport( + $arguments['reportLowUpperBound'], + $arguments['reportHighLowerBound'], + $arguments['coverageTextShowUncoveredFiles'], + $arguments['coverageTextShowOnlySummary'] + ); + + $outputStream->write( + $processor->process($codeCoverage, $colors) + ); + } + + if (isset($arguments['coverageXml'])) { + $this->printer->write( + "\nGenerating code coverage report in PHPUnit XML format ..." + ); + + try { + $writer = new XmlReport(Version::id()); + $writer->process($codeCoverage, $arguments['coverageXml']); + + $this->printer->write(" done\n"); + unset($writer); + } catch (CodeCoverageException $e) { + $this->printer->write( + " failed\n" . $e->getMessage() . "\n" + ); + } + } + } + + if ($exit) { + if ($result->wasSuccessfulIgnoringWarnings()) { + if ($arguments['failOnRisky'] && !$result->allHarmless()) { + exit(self::FAILURE_EXIT); + } + + if ($arguments['failOnWarning'] && $result->warningCount() > 0) { + exit(self::FAILURE_EXIT); + } + + exit(self::SUCCESS_EXIT); + } + + if ($result->errorCount() > 0) { + exit(self::EXCEPTION_EXIT); + } + + if ($result->failureCount() > 0) { + exit(self::FAILURE_EXIT); + } + } + + return $result; + } + + public function setPrinter(ResultPrinter $resultPrinter): void + { + $this->printer = $resultPrinter; + } + + /** + * Returns the loader to be used. + */ + public function getLoader(): TestSuiteLoader + { + if ($this->loader === null) { + $this->loader = new StandardTestSuiteLoader; + } + + return $this->loader; + } + + protected function createTestResult(): TestResult + { + return new TestResult; + } + + /** + * Override to define how to handle a failed loading of + * a test suite. + */ + protected function runFailed(string $message): void + { + $this->write($message . \PHP_EOL); + + exit(self::FAILURE_EXIT); + } + + protected function write(string $buffer): void + { + if (\PHP_SAPI != 'cli' && \PHP_SAPI != 'phpdbg') { + $buffer = \htmlspecialchars($buffer); + } + + if ($this->printer !== null) { + $this->printer->write($buffer); + } else { + print $buffer; + } + } + + /** + * @throws Exception + */ + protected function handleConfiguration(array &$arguments): void + { + if (isset($arguments['configuration']) && + !$arguments['configuration'] instanceof Configuration) { + $arguments['configuration'] = Configuration::getInstance( + $arguments['configuration'] + ); + } + + $arguments['debug'] = $arguments['debug'] ?? false; + $arguments['filter'] = $arguments['filter'] ?? false; + $arguments['listeners'] = $arguments['listeners'] ?? []; + + if (isset($arguments['configuration'])) { + $arguments['configuration']->handlePHPConfiguration(); + + $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration(); + + if (isset($phpunitConfiguration['backupGlobals']) && !isset($arguments['backupGlobals'])) { + $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals']; + } + + if (isset($phpunitConfiguration['backupStaticAttributes']) && !isset($arguments['backupStaticAttributes'])) { + $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes']; + } + + if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) && !isset($arguments['beStrictAboutChangesToGlobalState'])) { + $arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState']; + } + + if (isset($phpunitConfiguration['bootstrap']) && !isset($arguments['bootstrap'])) { + $arguments['bootstrap'] = $phpunitConfiguration['bootstrap']; + } + + if (isset($phpunitConfiguration['cacheResult']) && !isset($arguments['cacheResult'])) { + $arguments['cacheResult'] = $phpunitConfiguration['cacheResult']; + } + + if (isset($phpunitConfiguration['cacheResultFile']) && !isset($arguments['cacheResultFile'])) { + $arguments['cacheResultFile'] = $phpunitConfiguration['cacheResultFile']; + } + + if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) { + $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; + } + + if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) { + $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; + } + + if (isset($phpunitConfiguration['colors']) && !isset($arguments['colors'])) { + $arguments['colors'] = $phpunitConfiguration['colors']; + } + + if (isset($phpunitConfiguration['convertDeprecationsToExceptions']) && !isset($arguments['convertDeprecationsToExceptions'])) { + $arguments['convertDeprecationsToExceptions'] = $phpunitConfiguration['convertDeprecationsToExceptions']; + } + + if (isset($phpunitConfiguration['convertErrorsToExceptions']) && !isset($arguments['convertErrorsToExceptions'])) { + $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions']; + } + + if (isset($phpunitConfiguration['convertNoticesToExceptions']) && !isset($arguments['convertNoticesToExceptions'])) { + $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions']; + } + + if (isset($phpunitConfiguration['convertWarningsToExceptions']) && !isset($arguments['convertWarningsToExceptions'])) { + $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions']; + } + + if (isset($phpunitConfiguration['processIsolation']) && !isset($arguments['processIsolation'])) { + $arguments['processIsolation'] = $phpunitConfiguration['processIsolation']; + } + + if (isset($phpunitConfiguration['stopOnDefect']) && !isset($arguments['stopOnDefect'])) { + $arguments['stopOnDefect'] = $phpunitConfiguration['stopOnDefect']; + } + + if (isset($phpunitConfiguration['stopOnError']) && !isset($arguments['stopOnError'])) { + $arguments['stopOnError'] = $phpunitConfiguration['stopOnError']; + } + + if (isset($phpunitConfiguration['stopOnFailure']) && !isset($arguments['stopOnFailure'])) { + $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure']; + } + + if (isset($phpunitConfiguration['stopOnWarning']) && !isset($arguments['stopOnWarning'])) { + $arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning']; + } + + if (isset($phpunitConfiguration['stopOnIncomplete']) && !isset($arguments['stopOnIncomplete'])) { + $arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete']; + } + + if (isset($phpunitConfiguration['stopOnRisky']) && !isset($arguments['stopOnRisky'])) { + $arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky']; + } + + if (isset($phpunitConfiguration['stopOnSkipped']) && !isset($arguments['stopOnSkipped'])) { + $arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped']; + } + + if (isset($phpunitConfiguration['failOnWarning']) && !isset($arguments['failOnWarning'])) { + $arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning']; + } + + if (isset($phpunitConfiguration['failOnRisky']) && !isset($arguments['failOnRisky'])) { + $arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky']; + } + + if (isset($phpunitConfiguration['timeoutForSmallTests']) && !isset($arguments['timeoutForSmallTests'])) { + $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests']; + } + + if (isset($phpunitConfiguration['timeoutForMediumTests']) && !isset($arguments['timeoutForMediumTests'])) { + $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests']; + } + + if (isset($phpunitConfiguration['timeoutForLargeTests']) && !isset($arguments['timeoutForLargeTests'])) { + $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests']; + } + + if (isset($phpunitConfiguration['reportUselessTests']) && !isset($arguments['reportUselessTests'])) { + $arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests']; + } + + if (isset($phpunitConfiguration['strictCoverage']) && !isset($arguments['strictCoverage'])) { + $arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage']; + } + + if (isset($phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage']) && !isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { + $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage']; + } + + if (isset($phpunitConfiguration['disallowTestOutput']) && !isset($arguments['disallowTestOutput'])) { + $arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput']; + } + + if (isset($phpunitConfiguration['defaultTimeLimit']) && !isset($arguments['defaultTimeLimit'])) { + $arguments['defaultTimeLimit'] = $phpunitConfiguration['defaultTimeLimit']; + } + + if (isset($phpunitConfiguration['enforceTimeLimit']) && !isset($arguments['enforceTimeLimit'])) { + $arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit']; + } + + if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) && !isset($arguments['disallowTodoAnnotatedTests'])) { + $arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests']; + } + + if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) && !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) { + $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']; + } + + if (isset($phpunitConfiguration['verbose']) && !isset($arguments['verbose'])) { + $arguments['verbose'] = $phpunitConfiguration['verbose']; + } + + if (isset($phpunitConfiguration['reverseDefectList']) && !isset($arguments['reverseList'])) { + $arguments['reverseList'] = $phpunitConfiguration['reverseDefectList']; + } + + if (isset($phpunitConfiguration['forceCoversAnnotation']) && !isset($arguments['forceCoversAnnotation'])) { + $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation']; + } + + if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) && !isset($arguments['disableCodeCoverageIgnore'])) { + $arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore']; + } + + if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) && !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) { + $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']; + } + + if (isset($phpunitConfiguration['executionOrder']) && !isset($arguments['executionOrder'])) { + $arguments['executionOrder'] = $phpunitConfiguration['executionOrder']; + } + + if (isset($phpunitConfiguration['executionOrderDefects']) && !isset($arguments['executionOrderDefects'])) { + $arguments['executionOrderDefects'] = $phpunitConfiguration['executionOrderDefects']; + } + + if (isset($phpunitConfiguration['resolveDependencies']) && !isset($arguments['resolveDependencies'])) { + $arguments['resolveDependencies'] = $phpunitConfiguration['resolveDependencies']; + } + + $groupCliArgs = []; + + if (!empty($arguments['groups'])) { + $groupCliArgs = $arguments['groups']; + } + + $groupConfiguration = $arguments['configuration']->getGroupConfiguration(); + + if (!empty($groupConfiguration['include']) && !isset($arguments['groups'])) { + $arguments['groups'] = $groupConfiguration['include']; + } + + if (!empty($groupConfiguration['exclude']) && !isset($arguments['excludeGroups'])) { + $arguments['excludeGroups'] = \array_diff($groupConfiguration['exclude'], $groupCliArgs); + } + + foreach ($arguments['configuration']->getExtensionConfiguration() as $extension) { + if (!\class_exists($extension['class'], false) && $extension['file'] !== '') { + require_once $extension['file']; + } + + if (!\class_exists($extension['class'])) { + throw new Exception( + \sprintf( + 'Class "%s" does not exist', + $extension['class'] + ) + ); + } + + $extensionClass = new ReflectionClass($extension['class']); + + if (!$extensionClass->implementsInterface(Hook::class)) { + throw new Exception( + \sprintf( + 'Class "%s" does not implement a PHPUnit\Runner\Hook interface', + $extension['class'] + ) + ); + } + + if (\count($extension['arguments']) == 0) { + $this->extensions[] = $extensionClass->newInstance(); + } else { + $this->extensions[] = $extensionClass->newInstanceArgs( + $extension['arguments'] + ); + } + } + + foreach ($arguments['configuration']->getListenerConfiguration() as $listener) { + if (!\class_exists($listener['class'], false) && + $listener['file'] !== '') { + require_once $listener['file']; + } + + if (!\class_exists($listener['class'])) { + throw new Exception( + \sprintf( + 'Class "%s" does not exist', + $listener['class'] + ) + ); + } + + $listenerClass = new ReflectionClass($listener['class']); + + if (!$listenerClass->implementsInterface(TestListener::class)) { + throw new Exception( + \sprintf( + 'Class "%s" does not implement the PHPUnit\Framework\TestListener interface', + $listener['class'] + ) + ); + } + + if (\count($listener['arguments']) == 0) { + $listener = new $listener['class']; + } else { + $listener = $listenerClass->newInstanceArgs( + $listener['arguments'] + ); + } + + $arguments['listeners'][] = $listener; + } + + $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration(); + + if (isset($loggingConfiguration['coverage-clover']) && !isset($arguments['coverageClover'])) { + $arguments['coverageClover'] = $loggingConfiguration['coverage-clover']; + } + + if (isset($loggingConfiguration['coverage-crap4j']) && !isset($arguments['coverageCrap4J'])) { + $arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j']; + + if (isset($loggingConfiguration['crap4jThreshold']) && !isset($arguments['crap4jThreshold'])) { + $arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold']; + } + } + + if (isset($loggingConfiguration['coverage-html']) && !isset($arguments['coverageHtml'])) { + if (isset($loggingConfiguration['lowUpperBound']) && !isset($arguments['reportLowUpperBound'])) { + $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound']; + } + + if (isset($loggingConfiguration['highLowerBound']) && !isset($arguments['reportHighLowerBound'])) { + $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound']; + } + + $arguments['coverageHtml'] = $loggingConfiguration['coverage-html']; + } + + if (isset($loggingConfiguration['coverage-php']) && !isset($arguments['coveragePHP'])) { + $arguments['coveragePHP'] = $loggingConfiguration['coverage-php']; + } + + if (isset($loggingConfiguration['coverage-text']) && !isset($arguments['coverageText'])) { + $arguments['coverageText'] = $loggingConfiguration['coverage-text']; + + if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) { + $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles']; + } else { + $arguments['coverageTextShowUncoveredFiles'] = false; + } + + if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) { + $arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary']; + } else { + $arguments['coverageTextShowOnlySummary'] = false; + } + } + + if (isset($loggingConfiguration['coverage-xml']) && !isset($arguments['coverageXml'])) { + $arguments['coverageXml'] = $loggingConfiguration['coverage-xml']; + } + + if (isset($loggingConfiguration['plain'])) { + $arguments['listeners'][] = new ResultPrinter( + $loggingConfiguration['plain'], + true + ); + } + + if (isset($loggingConfiguration['teamcity']) && !isset($arguments['teamcityLogfile'])) { + $arguments['teamcityLogfile'] = $loggingConfiguration['teamcity']; + } + + if (isset($loggingConfiguration['junit']) && !isset($arguments['junitLogfile'])) { + $arguments['junitLogfile'] = $loggingConfiguration['junit']; + } + + if (isset($loggingConfiguration['testdox-html']) && !isset($arguments['testdoxHTMLFile'])) { + $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html']; + } + + if (isset($loggingConfiguration['testdox-text']) && !isset($arguments['testdoxTextFile'])) { + $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text']; + } + + if (isset($loggingConfiguration['testdox-xml']) && !isset($arguments['testdoxXMLFile'])) { + $arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml']; + } + + $testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration(); + + if (isset($testdoxGroupConfiguration['include']) && + !isset($arguments['testdoxGroups'])) { + $arguments['testdoxGroups'] = $testdoxGroupConfiguration['include']; + } + + if (isset($testdoxGroupConfiguration['exclude']) && + !isset($arguments['testdoxExcludeGroups'])) { + $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude']; + } + } + + $arguments['addUncoveredFilesFromWhitelist'] = $arguments['addUncoveredFilesFromWhitelist'] ?? true; + $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? null; + $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? null; + $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? null; + $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? false; + $arguments['cacheResult'] = $arguments['cacheResult'] ?? false; + $arguments['cacheTokens'] = $arguments['cacheTokens'] ?? false; + $arguments['colors'] = $arguments['colors'] ?? ResultPrinter::COLOR_DEFAULT; + $arguments['columns'] = $arguments['columns'] ?? 80; + $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? true; + $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? true; + $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? true; + $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? true; + $arguments['crap4jThreshold'] = $arguments['crap4jThreshold'] ?? 30; + $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? false; + $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? false; + $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? 0; + $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? false; + $arguments['excludeGroups'] = $arguments['excludeGroups'] ?? []; + $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? false; + $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? false; + $arguments['executionOrderDefects'] = $arguments['executionOrderDefects'] ?? TestSuiteSorter::ORDER_DEFAULT; + $arguments['groups'] = $arguments['groups'] ?? []; + $arguments['processIsolation'] = $arguments['processIsolation'] ?? false; + $arguments['processUncoveredFilesFromWhitelist'] = $arguments['processUncoveredFilesFromWhitelist'] ?? false; + $arguments['randomOrderSeed'] = $arguments['randomOrderSeed'] ?? \time(); + $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? false; + $arguments['repeat'] = $arguments['repeat'] ?? false; + $arguments['reportHighLowerBound'] = $arguments['reportHighLowerBound'] ?? 90; + $arguments['reportLowUpperBound'] = $arguments['reportLowUpperBound'] ?? 50; + $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? true; + $arguments['reverseList'] = $arguments['reverseList'] ?? false; + $arguments['executionOrder'] = $arguments['executionOrder'] ?? TestSuiteSorter::ORDER_DEFAULT; + $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? false; + $arguments['stopOnError'] = $arguments['stopOnError'] ?? false; + $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? false; + $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? false; + $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? false; + $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? false; + $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? false; + $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? false; + $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? false; + $arguments['testdoxExcludeGroups'] = $arguments['testdoxExcludeGroups'] ?? []; + $arguments['testdoxGroups'] = $arguments['testdoxGroups'] ?? []; + $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? 60; + $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? 10; + $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? 1; + $arguments['verbose'] = $arguments['verbose'] ?? false; + } + + /** + * @throws \ReflectionException + * @throws \InvalidArgumentException + */ + private function processSuiteFilters(TestSuite $suite, array $arguments): void + { + if (!$arguments['filter'] && + empty($arguments['groups']) && + empty($arguments['excludeGroups'])) { + return; + } + + $filterFactory = new Factory; + + if (!empty($arguments['excludeGroups'])) { + $filterFactory->addFilter( + new ReflectionClass(ExcludeGroupFilterIterator::class), + $arguments['excludeGroups'] + ); + } + + if (!empty($arguments['groups'])) { + $filterFactory->addFilter( + new ReflectionClass(IncludeGroupFilterIterator::class), + $arguments['groups'] + ); + } + + if ($arguments['filter']) { + $filterFactory->addFilter( + new ReflectionClass(NameFilterIterator::class), + $arguments['filter'] + ); + } + + $suite->injectFilter($filterFactory); + } + + private function writeMessage(string $type, string $message): void + { + if (!$this->messagePrinted) { + $this->write("\n"); + } + + $this->write( + \sprintf( + "%-15s%s\n", + $type . ':', + $message + ) + ); + + $this->messagePrinted = true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\TextUI; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestFailure; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\InvalidArgumentHelper; +use PHPUnit\Util\Printer; +use SebastianBergmann\Environment\Console; +use SebastianBergmann\Timer\Timer; + +/** + * Prints the result of a TextUI TestRunner run. + */ +class ResultPrinter extends Printer implements TestListener +{ + public const EVENT_TEST_START = 0; + + public const EVENT_TEST_END = 1; + + public const EVENT_TESTSUITE_START = 2; + + public const EVENT_TESTSUITE_END = 3; + + public const COLOR_NEVER = 'never'; + + public const COLOR_AUTO = 'auto'; + + public const COLOR_ALWAYS = 'always'; + + public const COLOR_DEFAULT = self::COLOR_NEVER; + + private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; + + /** + * @var array + */ + private static $ansiCodes = [ + 'bold' => 1, + 'fg-black' => 30, + 'fg-red' => 31, + 'fg-green' => 32, + 'fg-yellow' => 33, + 'fg-blue' => 34, + 'fg-magenta' => 35, + 'fg-cyan' => 36, + 'fg-white' => 37, + 'bg-black' => 40, + 'bg-red' => 41, + 'bg-green' => 42, + 'bg-yellow' => 43, + 'bg-blue' => 44, + 'bg-magenta' => 45, + 'bg-cyan' => 46, + 'bg-white' => 47, + ]; + + /** + * @var int + */ + protected $column = 0; + + /** + * @var int + */ + protected $maxColumn; + + /** + * @var bool + */ + protected $lastTestFailed = false; + + /** + * @var int + */ + protected $numAssertions = 0; + + /** + * @var int + */ + protected $numTests = -1; + + /** + * @var int + */ + protected $numTestsRun = 0; + + /** + * @var int + */ + protected $numTestsWidth; + + /** + * @var bool + */ + protected $colors = false; + + /** + * @var bool + */ + protected $debug = false; + + /** + * @var bool + */ + protected $verbose = false; + + /** + * @var int + */ + private $numberOfColumns; + + /** + * @var bool + */ + private $reverse; + + /** + * @var bool + */ + private $defectListPrinted = false; + + /** + * Constructor. + * + * @param string $colors + * @param int|string $numberOfColumns + * @param null|mixed $out + * + * @throws Exception + */ + public function __construct($out = null, bool $verbose = false, $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false) + { + parent::__construct($out); + + if (!\in_array($colors, self::AVAILABLE_COLORS, true)) { + throw InvalidArgumentHelper::factory( + 3, + \vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS) + ); + } + + if (!\is_int($numberOfColumns) && $numberOfColumns !== 'max') { + throw InvalidArgumentHelper::factory(5, 'integer or "max"'); + } + + $console = new Console; + $maxNumberOfColumns = $console->getNumberOfColumns(); + + if ($numberOfColumns === 'max' || ($numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns)) { + $numberOfColumns = $maxNumberOfColumns; + } + + $this->numberOfColumns = $numberOfColumns; + $this->verbose = $verbose; + $this->debug = $debug; + $this->reverse = $reverse; + + if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) { + $this->colors = true; + } else { + $this->colors = (self::COLOR_ALWAYS === $colors); + } + } + + public function printResult(TestResult $result): void + { + $this->printHeader(); + $this->printErrors($result); + $this->printWarnings($result); + $this->printFailures($result); + $this->printRisky($result); + + if ($this->verbose) { + $this->printIncompletes($result); + $this->printSkipped($result); + } + + $this->printFooter($result); + } + + /** + * An error occurred. + */ + public function addError(Test $test, \Throwable $t, float $time): void + { + $this->writeProgressWithColor('fg-red, bold', 'E'); + $this->lastTestFailed = true; + } + + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $this->writeProgressWithColor('bg-red, fg-white', 'F'); + $this->lastTestFailed = true; + } + + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + $this->writeProgressWithColor('fg-yellow, bold', 'W'); + $this->lastTestFailed = true; + } + + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + $this->writeProgressWithColor('fg-yellow, bold', 'I'); + $this->lastTestFailed = true; + } + + /** + * Risky test. + */ + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + $this->writeProgressWithColor('fg-yellow, bold', 'R'); + $this->lastTestFailed = true; + } + + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + $this->writeProgressWithColor('fg-cyan, bold', 'S'); + $this->lastTestFailed = true; + } + + /** + * A testsuite started. + */ + public function startTestSuite(TestSuite $suite): void + { + if ($this->numTests == -1) { + $this->numTests = \count($suite); + $this->numTestsWidth = \strlen((string) $this->numTests); + $this->maxColumn = $this->numberOfColumns - \strlen(' / (XXX%)') - (2 * $this->numTestsWidth); + } + } + + /** + * A testsuite ended. + */ + public function endTestSuite(TestSuite $suite): void + { + } + + /** + * A test started. + */ + public function startTest(Test $test): void + { + if ($this->debug) { + $this->write( + \sprintf( + "Test '%s' started\n", + \PHPUnit\Util\Test::describeAsString($test) + ) + ); + } + } + + /** + * A test ended. + */ + public function endTest(Test $test, float $time): void + { + if ($this->debug) { + $this->write( + \sprintf( + "Test '%s' ended\n", + \PHPUnit\Util\Test::describeAsString($test) + ) + ); + } + + if (!$this->lastTestFailed) { + $this->writeProgress('.'); + } + + if ($test instanceof TestCase) { + $this->numAssertions += $test->getNumAssertions(); + } elseif ($test instanceof PhptTestCase) { + $this->numAssertions++; + } + + $this->lastTestFailed = false; + + if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) { + $this->write($test->getActualOutput()); + } + } + + protected function printDefects(array $defects, string $type): void + { + $count = \count($defects); + + if ($count == 0) { + return; + } + + if ($this->defectListPrinted) { + $this->write("\n--\n\n"); + } + + $this->write( + \sprintf( + "There %s %d %s%s:\n", + ($count == 1) ? 'was' : 'were', + $count, + $type, + ($count == 1) ? '' : 's' + ) + ); + + $i = 1; + + if ($this->reverse) { + $defects = \array_reverse($defects); + } + + foreach ($defects as $defect) { + $this->printDefect($defect, $i++); + } + + $this->defectListPrinted = true; + } + + protected function printDefect(TestFailure $defect, int $count): void + { + $this->printDefectHeader($defect, $count); + $this->printDefectTrace($defect); + } + + protected function printDefectHeader(TestFailure $defect, int $count): void + { + $this->write( + \sprintf( + "\n%d) %s\n", + $count, + $defect->getTestName() + ) + ); + } + + protected function printDefectTrace(TestFailure $defect): void + { + $e = $defect->thrownException(); + $this->write((string) $e); + + while ($e = $e->getPrevious()) { + $this->write("\nCaused by\n" . $e); + } + } + + protected function printErrors(TestResult $result): void + { + $this->printDefects($result->errors(), 'error'); + } + + protected function printFailures(TestResult $result): void + { + $this->printDefects($result->failures(), 'failure'); + } + + protected function printWarnings(TestResult $result): void + { + $this->printDefects($result->warnings(), 'warning'); + } + + protected function printIncompletes(TestResult $result): void + { + $this->printDefects($result->notImplemented(), 'incomplete test'); + } + + protected function printRisky(TestResult $result): void + { + $this->printDefects($result->risky(), 'risky test'); + } + + protected function printSkipped(TestResult $result): void + { + $this->printDefects($result->skipped(), 'skipped test'); + } + + protected function printHeader(): void + { + $this->write("\n\n" . Timer::resourceUsage() . "\n\n"); + } + + protected function printFooter(TestResult $result): void + { + if (\count($result) === 0) { + $this->writeWithColor( + 'fg-black, bg-yellow', + 'No tests executed!' + ); + + return; + } + + if ($result->wasSuccessful() && + $result->allHarmless() && + $result->allCompletelyImplemented() && + $result->noneSkipped()) { + $this->writeWithColor( + 'fg-black, bg-green', + \sprintf( + 'OK (%d test%s, %d assertion%s)', + \count($result), + (\count($result) == 1) ? '' : 's', + $this->numAssertions, + ($this->numAssertions == 1) ? '' : 's' + ) + ); + } else { + if ($result->wasSuccessful()) { + $color = 'fg-black, bg-yellow'; + + if ($this->verbose || !$result->allHarmless()) { + $this->write("\n"); + } + + $this->writeWithColor( + $color, + 'OK, but incomplete, skipped, or risky tests!' + ); + } else { + $this->write("\n"); + + if ($result->errorCount()) { + $color = 'fg-white, bg-red'; + + $this->writeWithColor( + $color, + 'ERRORS!' + ); + } elseif ($result->failureCount()) { + $color = 'fg-white, bg-red'; + + $this->writeWithColor( + $color, + 'FAILURES!' + ); + } elseif ($result->warningCount()) { + $color = 'fg-black, bg-yellow'; + + $this->writeWithColor( + $color, + 'WARNINGS!' + ); + } + } + + $this->writeCountString(\count($result), 'Tests', $color, true); + $this->writeCountString($this->numAssertions, 'Assertions', $color, true); + $this->writeCountString($result->errorCount(), 'Errors', $color); + $this->writeCountString($result->failureCount(), 'Failures', $color); + $this->writeCountString($result->warningCount(), 'Warnings', $color); + $this->writeCountString($result->skippedCount(), 'Skipped', $color); + $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color); + $this->writeCountString($result->riskyCount(), 'Risky', $color); + $this->writeWithColor($color, '.'); + } + } + + protected function writeProgress(string $progress): void + { + if ($this->debug) { + return; + } + + $this->write($progress); + $this->column++; + $this->numTestsRun++; + + if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) { + if ($this->numTestsRun == $this->numTests) { + $this->write(\str_repeat(' ', $this->maxColumn - $this->column)); + } + + $this->write( + \sprintf( + ' %' . $this->numTestsWidth . 'd / %' . + $this->numTestsWidth . 'd (%3s%%)', + $this->numTestsRun, + $this->numTests, + \floor(($this->numTestsRun / $this->numTests) * 100) + ) + ); + + if ($this->column == $this->maxColumn) { + $this->writeNewLine(); + } + } + } + + protected function writeNewLine(): void + { + $this->column = 0; + $this->write("\n"); + } + + /** + * Formats a buffer with a specified ANSI color sequence if colors are + * enabled. + */ + protected function formatWithColor(string $color, string $buffer): string + { + if (!$this->colors) { + return $buffer; + } + + $codes = \array_map('\trim', \explode(',', $color)); + $lines = \explode("\n", $buffer); + $padding = \max(\array_map('\strlen', $lines)); + $styles = []; + + foreach ($codes as $code) { + $styles[] = self::$ansiCodes[$code]; + } + + $style = \sprintf("\x1b[%sm", \implode(';', $styles)); + + $styledLines = []; + + foreach ($lines as $line) { + $styledLines[] = $style . \str_pad($line, $padding) . "\x1b[0m"; + } + + return \implode("\n", $styledLines); + } + + /** + * Writes a buffer out with a color sequence if colors are enabled. + */ + protected function writeWithColor(string $color, string $buffer, bool $lf = true): void + { + $this->write($this->formatWithColor($color, $buffer)); + + if ($lf) { + $this->write("\n"); + } + } + + /** + * Writes progress with a color sequence if colors are enabled. + */ + protected function writeProgressWithColor(string $color, string $buffer): void + { + $buffer = $this->formatWithColor($color, $buffer); + $this->writeProgress($buffer); + } + + private function writeCountString(int $count, string $name, string $color, bool $always = false): void + { + static $first = true; + + if ($always || $count > 0) { + $this->writeWithColor( + $color, + \sprintf( + '%s%s: %d', + !$first ? ', ' : '', + $name, + $count + ), + false + ); + + $first = false; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +trait TestListenerDefaultImplementation +{ + public function addError(Test $test, \Throwable $t, float $time): void + { + } + + public function addWarning(Test $test, Warning $e, float $time): void + { + } + + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + } + + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + } + + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + } + + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + } + + public function startTestSuite(TestSuite $suite): void + { + } + + public function endTestSuite(TestSuite $suite): void + { + } + + public function startTest(Test $test): void + { + } + + public function endTest(Test $test, float $time): void + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class InvalidCoversTargetException extends CodeCoverageException +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +interface SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class OutputError extends AssertionFailedError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * Thrown when an assertion failed. + */ +class AssertionFailedError extends Exception implements SelfDescribing +{ + /** + * Wrapper for getMessage() which is declared as final. + */ + public function toString(): string + { + return $this->getMessage(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class MissingCoversAnnotationException extends RiskyTestError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class RiskyTestError extends AssertionFailedError implements RiskyTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +class Notice extends Error +{ + public static $enabled = true; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +class Deprecated extends Error +{ + public static $enabled = true; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +use PHPUnit\Framework\Exception; + +/** + * Wrapper for PHP errors. + */ +class Error extends Exception +{ + public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->file = $file; + $this->line = $line; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Error; + +class Warning extends Error +{ + public static $enabled = true; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * A Listener for test progress. + */ +interface TestListener +{ + /** + * An error occurred. + */ + public function addError(Test $test, \Throwable $t, float $time): void; + + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time): void; + + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void; + + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void; + + /** + * Risky test. + */ + public function addRiskyTest(Test $test, \Throwable $t, float $time): void; + + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, \Throwable $t, float $time): void; + + /** + * A test suite started. + */ + public function startTestSuite(TestSuite $suite): void; + + /** + * A test suite ended. + */ + public function endTestSuite(TestSuite $suite): void; + + /** + * A test started. + */ + public function startTest(Test $test): void; + + /** + * A test ended. + */ + public function endTest(Test $test, float $time): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * A marker interface for marking any exception/error as result of an unit + * test as incomplete implementation or currently not implemented. + */ +interface IncompleteTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * Creates a synthetic failed assertion. + */ +class SyntheticError extends AssertionFailedError +{ + /** + * The synthetic file. + * + * @var string + */ + protected $syntheticFile = ''; + + /** + * The synthetic line number. + * + * @var int + */ + protected $syntheticLine = 0; + + /** + * The synthetic trace. + * + * @var array + */ + protected $syntheticTrace = []; + + public function __construct(string $message, int $code, string $file, int $line, array $trace) + { + parent::__construct($message, $code); + + $this->syntheticFile = $file; + $this->syntheticLine = $line; + $this->syntheticTrace = $trace; + } + + public function getSyntheticFile(): string + { + return $this->syntheticFile; + } + + public function getSyntheticLine(): int + { + return $this->syntheticLine; + } + + public function getSyntheticTrace(): array + { + return $this->syntheticTrace; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class IncompleteTestError extends AssertionFailedError implements IncompleteTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use ArrayAccess; +use Countable; +use DOMDocument; +use DOMElement; +use PHPUnit\Framework\Constraint\ArrayHasKey; +use PHPUnit\Framework\Constraint\ArraySubset; +use PHPUnit\Framework\Constraint\Attribute; +use PHPUnit\Framework\Constraint\Callback; +use PHPUnit\Framework\Constraint\ClassHasAttribute; +use PHPUnit\Framework\Constraint\ClassHasStaticAttribute; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\Count; +use PHPUnit\Framework\Constraint\DirectoryExists; +use PHPUnit\Framework\Constraint\FileExists; +use PHPUnit\Framework\Constraint\GreaterThan; +use PHPUnit\Framework\Constraint\IsAnything; +use PHPUnit\Framework\Constraint\IsEmpty; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\Constraint\IsFalse; +use PHPUnit\Framework\Constraint\IsFinite; +use PHPUnit\Framework\Constraint\IsIdentical; +use PHPUnit\Framework\Constraint\IsInfinite; +use PHPUnit\Framework\Constraint\IsInstanceOf; +use PHPUnit\Framework\Constraint\IsJson; +use PHPUnit\Framework\Constraint\IsNan; +use PHPUnit\Framework\Constraint\IsNull; +use PHPUnit\Framework\Constraint\IsReadable; +use PHPUnit\Framework\Constraint\IsTrue; +use PHPUnit\Framework\Constraint\IsType; +use PHPUnit\Framework\Constraint\IsWritable; +use PHPUnit\Framework\Constraint\JsonMatches; +use PHPUnit\Framework\Constraint\LessThan; +use PHPUnit\Framework\Constraint\LogicalAnd; +use PHPUnit\Framework\Constraint\LogicalNot; +use PHPUnit\Framework\Constraint\LogicalOr; +use PHPUnit\Framework\Constraint\LogicalXor; +use PHPUnit\Framework\Constraint\ObjectHasAttribute; +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\SameSize; +use PHPUnit\Framework\Constraint\StringContains; +use PHPUnit\Framework\Constraint\StringEndsWith; +use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; +use PHPUnit\Framework\Constraint\StringStartsWith; +use PHPUnit\Framework\Constraint\TraversableContains; +use PHPUnit\Framework\Constraint\TraversableContainsOnly; +use PHPUnit\Util\InvalidArgumentHelper; +use PHPUnit\Util\Type; +use PHPUnit\Util\Xml; +use ReflectionClass; +use ReflectionException; +use ReflectionObject; +use Traversable; + +/** + * A set of assertion methods. + */ +abstract class Assert +{ + /** + * @var int + */ + private static $count = 0; + + /** + * Asserts that an array has a specified key. + * + * @param int|string $key + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertArrayHasKey($key, $array, string $message = ''): void + { + if (!(\is_int($key) || \is_string($key))) { + throw InvalidArgumentHelper::factory( + 1, + 'integer or string' + ); + } + + if (!(\is_array($array) || $array instanceof ArrayAccess)) { + throw InvalidArgumentHelper::factory( + 2, + 'array or ArrayAccess' + ); + } + + $constraint = new ArrayHasKey($key); + + static::assertThat($array, $constraint, $message); + } + + /** + * Asserts that an array has a specified subset. + * + * @param array|ArrayAccess $subset + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494 + */ + public static function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void + { + if (!(\is_array($subset) || $subset instanceof ArrayAccess)) { + throw InvalidArgumentHelper::factory( + 1, + 'array or ArrayAccess' + ); + } + + if (!(\is_array($array) || $array instanceof ArrayAccess)) { + throw InvalidArgumentHelper::factory( + 2, + 'array or ArrayAccess' + ); + } + + $constraint = new ArraySubset($subset, $checkForObjectIdentity); + + static::assertThat($array, $constraint, $message); + } + + /** + * Asserts that an array does not have a specified key. + * + * @param int|string $key + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertArrayNotHasKey($key, $array, string $message = ''): void + { + if (!(\is_int($key) || \is_string($key))) { + throw InvalidArgumentHelper::factory( + 1, + 'integer or string' + ); + } + + if (!(\is_array($array) || $array instanceof ArrayAccess)) { + throw InvalidArgumentHelper::factory( + 2, + 'array or ArrayAccess' + ); + } + + $constraint = new LogicalNot( + new ArrayHasKey($key) + ); + + static::assertThat($array, $constraint, $message); + } + + /** + * Asserts that a haystack contains a needle. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void + { + if (\is_array($haystack) || + (\is_object($haystack) && $haystack instanceof Traversable)) { + $constraint = new TraversableContains( + $needle, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ); + } elseif (\is_string($haystack)) { + if (!\is_string($needle)) { + throw InvalidArgumentHelper::factory( + 1, + 'string' + ); + } + + $constraint = new StringContains( + $needle, + $ignoreCase + ); + } else { + throw InvalidArgumentHelper::factory( + 2, + 'array, traversable or string' + ); + } + + static::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains a needle. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void + { + static::assertContains( + $needle, + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message, + $ignoreCase, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ); + } + + /** + * Asserts that a haystack does not contain a needle. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void + { + if (\is_array($haystack) || + (\is_object($haystack) && $haystack instanceof Traversable)) { + $constraint = new LogicalNot( + new TraversableContains( + $needle, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ) + ); + } elseif (\is_string($haystack)) { + if (!\is_string($needle)) { + throw InvalidArgumentHelper::factory( + 1, + 'string' + ); + } + + $constraint = new LogicalNot( + new StringContains( + $needle, + $ignoreCase + ) + ); + } else { + throw InvalidArgumentHelper::factory( + 2, + 'array, traversable or string' + ); + } + + static::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain a needle. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void + { + static::assertNotContains( + $needle, + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message, + $ignoreCase, + $checkForObjectIdentity, + $checkForNonObjectIdentity + ); + } + + /** + * Asserts that a haystack contains only values of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + { + if ($isNativeType === null) { + $isNativeType = Type::isType($type); + } + + static::assertThat( + $haystack, + new TraversableContainsOnly( + $type, + $isNativeType + ), + $message + ); + } + + /** + * Asserts that a haystack contains only instances of a given class name. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void + { + static::assertThat( + $haystack, + new TraversableContainsOnly( + $className, + false + ), + $message + ); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains only values of a given type. + * + * @param object|string $haystackClassOrObject + * @param bool $isNativeType + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void + { + static::assertContainsOnly( + $type, + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $isNativeType, + $message + ); + } + + /** + * Asserts that a haystack does not contain only values of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void + { + if ($isNativeType === null) { + $isNativeType = Type::isType($type); + } + + static::assertThat( + $haystack, + new LogicalNot( + new TraversableContainsOnly( + $type, + $isNativeType + ) + ), + $message + ); + } + + /** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain only values of a given + * type. + * + * @param object|string $haystackClassOrObject + * @param bool $isNativeType + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void + { + static::assertNotContainsOnly( + $type, + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $isNativeType, + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertCount(int $expectedCount, $haystack, string $message = ''): void + { + if (!$haystack instanceof Countable && !\is_iterable($haystack)) { + throw InvalidArgumentHelper::factory(2, 'countable or iterable'); + } + + static::assertThat( + $haystack, + new Count($expectedCount), + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void + { + static::assertCount( + $expectedCount, + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotCount(int $expectedCount, $haystack, string $message = ''): void + { + if (!$haystack instanceof Countable && !\is_iterable($haystack)) { + throw InvalidArgumentHelper::factory(2, 'countable or iterable'); + } + + $constraint = new LogicalNot( + new Count($expectedCount) + ); + + static::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void + { + static::assertNotCount( + $expectedCount, + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that two variables are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void + { + $constraint = new IsEqual( + $expected, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two variables are equal (canonicalizing). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void + { + $constraint = new IsEqual( + $expected, + 0.0, + 10, + true, + false + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two variables are equal (ignoring case). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void + { + $constraint = new IsEqual( + $expected, + 0.0, + 10, + false, + true + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two variables are equal (with delta). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void + { + $constraint = new IsEqual( + $expected, + $delta + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a variable is equal to an attribute of an object. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void + { + static::assertEquals( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that two variables are not equal. + * + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void + { + $constraint = new LogicalNot( + new IsEqual( + $expected, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ) + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two variables are not equal (canonicalizing). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void + { + $constraint = new LogicalNot( + new IsEqual( + $expected, + 0.0, + 10, + true, + false + ) + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two variables are not equal (ignoring case). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void + { + $constraint = new LogicalNot( + new IsEqual( + $expected, + 0.0, + 10, + false, + true + ) + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that two variables are not equal (with delta). + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void + { + $constraint = new LogicalNot( + new IsEqual( + $expected, + $delta + ) + ); + + static::assertThat($actual, $constraint, $message); + } + + /** + * Asserts that a variable is not equal to an attribute of an object. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void + { + static::assertNotEquals( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that a variable is empty. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertEmpty($actual, string $message = ''): void + { + static::assertThat($actual, static::isEmpty(), $message); + } + + /** + * Asserts that a static attribute of a class or an attribute of an object + * is empty. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void + { + static::assertEmpty( + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that a variable is not empty. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotEmpty($actual, string $message = ''): void + { + static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); + } + + /** + * Asserts that a static attribute of a class or an attribute of an object + * is not empty. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void + { + static::assertNotEmpty( + static::readAttribute($haystackClassOrObject, $haystackAttributeName), + $message + ); + } + + /** + * Asserts that a value is greater than another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertGreaterThan($expected, $actual, string $message = ''): void + { + static::assertThat($actual, static::greaterThan($expected), $message); + } + + /** + * Asserts that an attribute is greater than another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void + { + static::assertGreaterThan( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is greater than or equal to another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void + { + static::assertThat( + $actual, + static::greaterThanOrEqual($expected), + $message + ); + } + + /** + * Asserts that an attribute is greater than or equal to another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void + { + static::assertGreaterThanOrEqual( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is smaller than another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertLessThan($expected, $actual, string $message = ''): void + { + static::assertThat($actual, static::lessThan($expected), $message); + } + + /** + * Asserts that an attribute is smaller than another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void + { + static::assertLessThan( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a value is smaller than or equal to another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertLessThanOrEqual($expected, $actual, string $message = ''): void + { + static::assertThat($actual, static::lessThanOrEqual($expected), $message); + } + + /** + * Asserts that an attribute is smaller than or equal to another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void + { + static::assertLessThanOrEqual( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void + { + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + + static::assertEquals( + \file_get_contents($expected), + \file_get_contents($actual), + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void + { + static::assertFileExists($expected, $message); + static::assertFileExists($actual, $message); + + static::assertNotEquals( + \file_get_contents($expected), + \file_get_contents($actual), + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of a string is equal + * to the contents of a file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void + { + static::assertFileExists($expectedFile, $message); + + /** @noinspection PhpUnitTestsInspection */ + static::assertEquals( + \file_get_contents($expectedFile), + $actualString, + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void + { + static::assertFileExists($expectedFile, $message); + + static::assertNotEquals( + \file_get_contents($expectedFile), + $actualString, + $message, + 0, + 10, + $canonicalize, + $ignoreCase + ); + } + + /** + * Asserts that a file/dir is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertIsReadable(string $filename, string $message = ''): void + { + static::assertThat($filename, new IsReadable, $message); + } + + /** + * Asserts that a file/dir exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotIsReadable(string $filename, string $message = ''): void + { + static::assertThat($filename, new LogicalNot(new IsReadable), $message); + } + + /** + * Asserts that a file/dir exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertIsWritable(string $filename, string $message = ''): void + { + static::assertThat($filename, new IsWritable, $message); + } + + /** + * Asserts that a file/dir exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotIsWritable(string $filename, string $message = ''): void + { + static::assertThat($filename, new LogicalNot(new IsWritable), $message); + } + + /** + * Asserts that a directory exists. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDirectoryExists(string $directory, string $message = ''): void + { + static::assertThat($directory, new DirectoryExists, $message); + } + + /** + * Asserts that a directory does not exist. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDirectoryNotExists(string $directory, string $message = ''): void + { + static::assertThat($directory, new LogicalNot(new DirectoryExists), $message); + } + + /** + * Asserts that a directory exists and is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDirectoryIsReadable(string $directory, string $message = ''): void + { + self::assertDirectoryExists($directory, $message); + self::assertIsReadable($directory, $message); + } + + /** + * Asserts that a directory exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDirectoryNotIsReadable(string $directory, string $message = ''): void + { + self::assertDirectoryExists($directory, $message); + self::assertNotIsReadable($directory, $message); + } + + /** + * Asserts that a directory exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDirectoryIsWritable(string $directory, string $message = ''): void + { + self::assertDirectoryExists($directory, $message); + self::assertIsWritable($directory, $message); + } + + /** + * Asserts that a directory exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertDirectoryNotIsWritable(string $directory, string $message = ''): void + { + self::assertDirectoryExists($directory, $message); + self::assertNotIsWritable($directory, $message); + } + + /** + * Asserts that a file exists. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileExists(string $filename, string $message = ''): void + { + static::assertThat($filename, new FileExists, $message); + } + + /** + * Asserts that a file does not exist. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileNotExists(string $filename, string $message = ''): void + { + static::assertThat($filename, new LogicalNot(new FileExists), $message); + } + + /** + * Asserts that a file exists and is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileIsReadable(string $file, string $message = ''): void + { + self::assertFileExists($file, $message); + self::assertIsReadable($file, $message); + } + + /** + * Asserts that a file exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileNotIsReadable(string $file, string $message = ''): void + { + self::assertFileExists($file, $message); + self::assertNotIsReadable($file, $message); + } + + /** + * Asserts that a file exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileIsWritable(string $file, string $message = ''): void + { + self::assertFileExists($file, $message); + self::assertIsWritable($file, $message); + } + + /** + * Asserts that a file exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFileNotIsWritable(string $file, string $message = ''): void + { + self::assertFileExists($file, $message); + self::assertNotIsWritable($file, $message); + } + + /** + * Asserts that a condition is true. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertTrue($condition, string $message = ''): void + { + static::assertThat($condition, static::isTrue(), $message); + } + + /** + * Asserts that a condition is not true. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotTrue($condition, string $message = ''): void + { + static::assertThat($condition, static::logicalNot(static::isTrue()), $message); + } + + /** + * Asserts that a condition is false. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFalse($condition, string $message = ''): void + { + static::assertThat($condition, static::isFalse(), $message); + } + + /** + * Asserts that a condition is not false. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotFalse($condition, string $message = ''): void + { + static::assertThat($condition, static::logicalNot(static::isFalse()), $message); + } + + /** + * Asserts that a variable is null. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNull($actual, string $message = ''): void + { + static::assertThat($actual, static::isNull(), $message); + } + + /** + * Asserts that a variable is not null. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotNull($actual, string $message = ''): void + { + static::assertThat($actual, static::logicalNot(static::isNull()), $message); + } + + /** + * Asserts that a variable is finite. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertFinite($actual, string $message = ''): void + { + static::assertThat($actual, static::isFinite(), $message); + } + + /** + * Asserts that a variable is infinite. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertInfinite($actual, string $message = ''): void + { + static::assertThat($actual, static::isInfinite(), $message); + } + + /** + * Asserts that a variable is nan. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNan($actual, string $message = ''): void + { + static::assertThat($actual, static::isNan(), $message); + } + + /** + * Asserts that a class has a specified attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!\class_exists($className)) { + throw InvalidArgumentHelper::factory(2, 'class name', $className); + } + + static::assertThat($className, new ClassHasAttribute($attributeName), $message); + } + + /** + * Asserts that a class does not have a specified attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!\class_exists($className)) { + throw InvalidArgumentHelper::factory(2, 'class name', $className); + } + + static::assertThat( + $className, + new LogicalNot( + new ClassHasAttribute($attributeName) + ), + $message + ); + } + + /** + * Asserts that a class has a specified static attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!\class_exists($className)) { + throw InvalidArgumentHelper::factory(2, 'class name', $className); + } + + static::assertThat( + $className, + new ClassHasStaticAttribute($attributeName), + $message + ); + } + + /** + * Asserts that a class does not have a specified static attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!\class_exists($className)) { + throw InvalidArgumentHelper::factory(2, 'class name', $className); + } + + static::assertThat( + $className, + new LogicalNot( + new ClassHasStaticAttribute($attributeName) + ), + $message + ); + } + + /** + * Asserts that an object has a specified attribute. + * + * @param object $object + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!\is_object($object)) { + throw InvalidArgumentHelper::factory(2, 'object'); + } + + static::assertThat( + $object, + new ObjectHasAttribute($attributeName), + $message + ); + } + + /** + * Asserts that an object does not have a specified attribute. + * + * @param object $object + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(1, 'valid attribute name'); + } + + if (!\is_object($object)) { + throw InvalidArgumentHelper::factory(2, 'object'); + } + + static::assertThat( + $object, + new LogicalNot( + new ObjectHasAttribute($attributeName) + ), + $message + ); + } + + /** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertSame($expected, $actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsIdentical($expected), + $message + ); + } + + /** + * Asserts that a variable and an attribute of an object have the same type + * and value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void + { + static::assertSame( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotSame($expected, $actual, string $message = ''): void + { + if (\is_bool($expected) && \is_bool($actual)) { + static::assertNotEquals($expected, $actual, $message); + } + + static::assertThat( + $actual, + new LogicalNot( + new IsIdentical($expected) + ), + $message + ); + } + + /** + * Asserts that a variable and an attribute of an object do not have the + * same type and value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void + { + static::assertNotSame( + $expected, + static::readAttribute($actualClassOrObject, $actualAttributeName), + $message + ); + } + + /** + * Asserts that a variable is of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertInstanceOf(string $expected, $actual, string $message = ''): void + { + if (!\class_exists($expected) && !\interface_exists($expected)) { + throw InvalidArgumentHelper::factory(1, 'class or interface name'); + } + + static::assertThat( + $actual, + new IsInstanceOf($expected), + $message + ); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void + { + static::assertInstanceOf( + $expected, + static::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is not of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void + { + if (!\class_exists($expected) && !\interface_exists($expected)) { + throw InvalidArgumentHelper::factory(1, 'class or interface name'); + } + + static::assertThat( + $actual, + new LogicalNot( + new IsInstanceOf($expected) + ), + $message + ); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void + { + static::assertNotInstanceOf( + $expected, + static::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369 + */ + public static function assertInternalType(string $expected, $actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType($expected), + $message + ); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void + { + static::assertInternalType( + $expected, + static::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a variable is of type array. + */ + public static function assertIsArray($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_ARRAY), + $message + ); + } + + /** + * Asserts that a variable is of type bool. + */ + public static function assertIsBool($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_BOOL), + $message + ); + } + + /** + * Asserts that a variable is of type float. + */ + public static function assertIsFloat($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_FLOAT), + $message + ); + } + + /** + * Asserts that a variable is of type int. + */ + public static function assertIsInt($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_INT), + $message + ); + } + + /** + * Asserts that a variable is of type numeric. + */ + public static function assertIsNumeric($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_NUMERIC), + $message + ); + } + + /** + * Asserts that a variable is of type object. + */ + public static function assertIsObject($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_OBJECT), + $message + ); + } + + /** + * Asserts that a variable is of type resource. + */ + public static function assertIsResource($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_RESOURCE), + $message + ); + } + + /** + * Asserts that a variable is of type string. + */ + public static function assertIsString($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_STRING), + $message + ); + } + + /** + * Asserts that a variable is of type scalar. + */ + public static function assertIsScalar($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_SCALAR), + $message + ); + } + + /** + * Asserts that a variable is of type callable. + */ + public static function assertIsCallable($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_CALLABLE), + $message + ); + } + + /** + * Asserts that a variable is of type iterable. + */ + public static function assertIsIterable($actual, string $message = ''): void + { + static::assertThat( + $actual, + new IsType(IsType::TYPE_ITERABLE), + $message + ); + } + + /** + * Asserts that a variable is not of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369 + */ + public static function assertNotInternalType(string $expected, $actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot( + new IsType($expected) + ), + $message + ); + } + + /** + * Asserts that a variable is not of type array. + */ + public static function assertIsNotArray($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_ARRAY)), + $message + ); + } + + /** + * Asserts that a variable is not of type bool. + */ + public static function assertIsNotBool($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_BOOL)), + $message + ); + } + + /** + * Asserts that a variable is not of type float. + */ + public static function assertIsNotFloat($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_FLOAT)), + $message + ); + } + + /** + * Asserts that a variable is not of type int. + */ + public static function assertIsNotInt($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_INT)), + $message + ); + } + + /** + * Asserts that a variable is not of type numeric. + */ + public static function assertIsNotNumeric($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), + $message + ); + } + + /** + * Asserts that a variable is not of type object. + */ + public static function assertIsNotObject($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_OBJECT)), + $message + ); + } + + /** + * Asserts that a variable is not of type resource. + */ + public static function assertIsNotResource($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), + $message + ); + } + + /** + * Asserts that a variable is not of type string. + */ + public static function assertIsNotString($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_STRING)), + $message + ); + } + + /** + * Asserts that a variable is not of type scalar. + */ + public static function assertIsNotScalar($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_SCALAR)), + $message + ); + } + + /** + * Asserts that a variable is not of type callable. + */ + public static function assertIsNotCallable($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), + $message + ); + } + + /** + * Asserts that a variable is not of type iterable. + */ + public static function assertIsNotIterable($actual, string $message = ''): void + { + static::assertThat( + $actual, + new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), + $message + ); + } + + /** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void + { + static::assertNotInternalType( + $expected, + static::readAttribute($classOrObject, $attributeName), + $message + ); + } + + /** + * Asserts that a string matches a given regular expression. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertRegExp(string $pattern, string $string, string $message = ''): void + { + static::assertThat($string, new RegularExpression($pattern), $message); + } + + /** + * Asserts that a string does not match a given regular expression. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotRegExp(string $pattern, string $string, string $message = ''): void + { + static::assertThat( + $string, + new LogicalNot( + new RegularExpression($pattern) + ), + $message + ); + } + + /** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertSameSize($expected, $actual, string $message = ''): void + { + if (!$expected instanceof Countable && !\is_iterable($expected)) { + throw InvalidArgumentHelper::factory(1, 'countable or iterable'); + } + + if (!$actual instanceof Countable && !\is_iterable($actual)) { + throw InvalidArgumentHelper::factory(2, 'countable or iterable'); + } + + static::assertThat( + $actual, + new SameSize($expected), + $message + ); + } + + /** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. + * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertNotSameSize($expected, $actual, string $message = ''): void + { + if (!$expected instanceof Countable && !\is_iterable($expected)) { + throw InvalidArgumentHelper::factory(1, 'countable or iterable'); + } + + if (!$actual instanceof Countable && !\is_iterable($actual)) { + throw InvalidArgumentHelper::factory(2, 'countable or iterable'); + } + + static::assertThat( + $actual, + new LogicalNot( + new SameSize($expected) + ), + $message + ); + } + + /** + * Asserts that a string matches a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void + { + static::assertThat($string, new StringMatchesFormatDescription($format), $message); + } + + /** + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void + { + static::assertThat( + $string, + new LogicalNot( + new StringMatchesFormatDescription($format) + ), + $message + ); + } + + /** + * Asserts that a string matches a given format file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void + { + static::assertFileExists($formatFile, $message); + + static::assertThat( + $string, + new StringMatchesFormatDescription( + \file_get_contents($formatFile) + ), + $message + ); + } + + /** + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void + { + static::assertFileExists($formatFile, $message); + + static::assertThat( + $string, + new LogicalNot( + new StringMatchesFormatDescription( + \file_get_contents($formatFile) + ) + ), + $message + ); + } + + /** + * Asserts that a string starts with a given prefix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void + { + static::assertThat($string, new StringStartsWith($prefix), $message); + } + + /** + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringStartsNotWith($prefix, $string, string $message = ''): void + { + static::assertThat( + $string, + new LogicalNot( + new StringStartsWith($prefix) + ), + $message + ); + } + + public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void + { + $constraint = new StringContains($needle, false); + + static::assertThat($haystack, $constraint, $message); + } + + public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void + { + $constraint = new StringContains($needle, true); + + static::assertThat($haystack, $constraint, $message); + } + + public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void + { + $constraint = new LogicalNot(new StringContains($needle)); + + static::assertThat($haystack, $constraint, $message); + } + + public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void + { + $constraint = new LogicalNot(new StringContains($needle, true)); + + static::assertThat($haystack, $constraint, $message); + } + + /** + * Asserts that a string ends with a given suffix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void + { + static::assertThat($string, new StringEndsWith($suffix), $message); + } + + /** + * Asserts that a string ends not with a given suffix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void + { + static::assertThat( + $string, + new LogicalNot( + new StringEndsWith($suffix) + ), + $message + ); + } + + /** + * Asserts that two XML files are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void + { + $expected = Xml::loadFile($expectedFile); + $actual = Xml::loadFile($actualFile); + + static::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML files are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void + { + $expected = Xml::loadFile($expectedFile); + $actual = Xml::loadFile($actualFile); + + static::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void + { + $expected = Xml::loadFile($expectedFile); + $actual = Xml::load($actualXml); + + static::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are not equal. + * + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void + { + $expected = Xml::loadFile($expectedFile); + $actual = Xml::load($actualXml); + + static::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void + { + $expected = Xml::load($expectedXml); + $actual = Xml::load($actualXml); + + static::assertEquals($expected, $actual, $message); + } + + /** + * Asserts that two XML documents are not equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void + { + $expected = Xml::load($expectedXml); + $actual = Xml::load($actualXml); + + static::assertNotEquals($expected, $actual, $message); + } + + /** + * Asserts that a hierarchy of DOMElements matches. + * + * @throws AssertionFailedError + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void + { + $expectedElement = Xml::import($expectedElement); + $actualElement = Xml::import($actualElement); + + static::assertSame( + $expectedElement->tagName, + $actualElement->tagName, + $message + ); + + if ($checkAttributes) { + static::assertSame( + $expectedElement->attributes->length, + $actualElement->attributes->length, + \sprintf( + '%s%sNumber of attributes on node "%s" does not match', + $message, + !empty($message) ? "\n" : '', + $expectedElement->tagName + ) + ); + + for ($i = 0; $i < $expectedElement->attributes->length; $i++) { + /** @var \DOMAttr $expectedAttribute */ + $expectedAttribute = $expectedElement->attributes->item($i); + + /** @var \DOMAttr $actualAttribute */ + $actualAttribute = $actualElement->attributes->getNamedItem( + $expectedAttribute->name + ); + + if (!$actualAttribute) { + static::fail( + \sprintf( + '%s%sCould not find attribute "%s" on node "%s"', + $message, + !empty($message) ? "\n" : '', + $expectedAttribute->name, + $expectedElement->tagName + ) + ); + } + } + } + + Xml::removeCharacterDataNodes($expectedElement); + Xml::removeCharacterDataNodes($actualElement); + + static::assertSame( + $expectedElement->childNodes->length, + $actualElement->childNodes->length, + \sprintf( + '%s%sNumber of child nodes of "%s" differs', + $message, + !empty($message) ? "\n" : '', + $expectedElement->tagName + ) + ); + + for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { + static::assertEqualXMLStructure( + $expectedElement->childNodes->item($i), + $actualElement->childNodes->item($i), + $checkAttributes, + $message + ); + } + } + + /** + * Evaluates a PHPUnit\Framework\Constraint matcher object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertThat($value, Constraint $constraint, string $message = ''): void + { + self::$count += \count($constraint); + + $constraint->evaluate($value, $message); + } + + /** + * Asserts that a string is a valid JSON string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJson(string $actualJson, string $message = ''): void + { + static::assertThat($actualJson, static::isJson(), $message); + } + + /** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void + { + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + } + + /** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void + { + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + + static::assertThat( + $actualJson, + new LogicalNot( + new JsonMatches($expectedJson) + ), + $message + ); + } + + /** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void + { + static::assertFileExists($expectedFile, $message); + $expectedJson = \file_get_contents($expectedFile); + + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + + static::assertThat($actualJson, new JsonMatches($expectedJson), $message); + } + + /** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void + { + static::assertFileExists($expectedFile, $message); + $expectedJson = \file_get_contents($expectedFile); + + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + + static::assertThat( + $actualJson, + new LogicalNot( + new JsonMatches($expectedJson) + ), + $message + ); + } + + /** + * Asserts that two JSON files are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void + { + static::assertFileExists($expectedFile, $message); + static::assertFileExists($actualFile, $message); + + $actualJson = \file_get_contents($actualFile); + $expectedJson = \file_get_contents($expectedFile); + + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + + $constraintExpected = new JsonMatches( + $expectedJson + ); + + $constraintActual = new JsonMatches($actualJson); + + static::assertThat($expectedJson, $constraintActual, $message); + static::assertThat($actualJson, $constraintExpected, $message); + } + + /** + * Asserts that two JSON files are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void + { + static::assertFileExists($expectedFile, $message); + static::assertFileExists($actualFile, $message); + + $actualJson = \file_get_contents($actualFile); + $expectedJson = \file_get_contents($expectedFile); + + static::assertJson($expectedJson, $message); + static::assertJson($actualJson, $message); + + $constraintExpected = new JsonMatches( + $expectedJson + ); + + $constraintActual = new JsonMatches($actualJson); + + static::assertThat($expectedJson, new LogicalNot($constraintActual), $message); + static::assertThat($actualJson, new LogicalNot($constraintExpected), $message); + } + + public static function logicalAnd(): LogicalAnd + { + $constraints = \func_get_args(); + + $constraint = new LogicalAnd; + $constraint->setConstraints($constraints); + + return $constraint; + } + + public static function logicalOr(): LogicalOr + { + $constraints = \func_get_args(); + + $constraint = new LogicalOr; + $constraint->setConstraints($constraints); + + return $constraint; + } + + public static function logicalNot(Constraint $constraint): LogicalNot + { + return new LogicalNot($constraint); + } + + public static function logicalXor(): LogicalXor + { + $constraints = \func_get_args(); + + $constraint = new LogicalXor; + $constraint->setConstraints($constraints); + + return $constraint; + } + + public static function anything(): IsAnything + { + return new IsAnything; + } + + public static function isTrue(): IsTrue + { + return new IsTrue; + } + + public static function callback(callable $callback): Callback + { + return new Callback($callback); + } + + public static function isFalse(): IsFalse + { + return new IsFalse; + } + + public static function isJson(): IsJson + { + return new IsJson; + } + + public static function isNull(): IsNull + { + return new IsNull; + } + + public static function isFinite(): IsFinite + { + return new IsFinite; + } + + public static function isInfinite(): IsInfinite + { + return new IsInfinite; + } + + public static function isNan(): IsNan + { + return new IsNan; + } + + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function attribute(Constraint $constraint, string $attributeName): Attribute + { + return new Attribute($constraint, $attributeName); + } + + public static function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains + { + return new TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity); + } + + public static function containsOnly(string $type): TraversableContainsOnly + { + return new TraversableContainsOnly($type); + } + + public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly + { + return new TraversableContainsOnly($className, false); + } + + /** + * @param int|string $key + */ + public static function arrayHasKey($key): ArrayHasKey + { + return new ArrayHasKey($key); + } + + public static function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual + { + return new IsEqual($value, $delta, $maxDepth, $canonicalize, $ignoreCase); + } + + /** + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute + { + return static::attribute( + static::equalTo( + $value, + $delta, + $maxDepth, + $canonicalize, + $ignoreCase + ), + $attributeName + ); + } + + public static function isEmpty(): IsEmpty + { + return new IsEmpty; + } + + public static function isWritable(): IsWritable + { + return new IsWritable; + } + + public static function isReadable(): IsReadable + { + return new IsReadable; + } + + public static function directoryExists(): DirectoryExists + { + return new DirectoryExists; + } + + public static function fileExists(): FileExists + { + return new FileExists; + } + + public static function greaterThan($value): GreaterThan + { + return new GreaterThan($value); + } + + public static function greaterThanOrEqual($value): LogicalOr + { + return static::logicalOr( + new IsEqual($value), + new GreaterThan($value) + ); + } + + public static function classHasAttribute(string $attributeName): ClassHasAttribute + { + return new ClassHasAttribute($attributeName); + } + + public static function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute + { + return new ClassHasStaticAttribute($attributeName); + } + + public static function objectHasAttribute($attributeName): ObjectHasAttribute + { + return new ObjectHasAttribute($attributeName); + } + + public static function identicalTo($value): IsIdentical + { + return new IsIdentical($value); + } + + public static function isInstanceOf(string $className): IsInstanceOf + { + return new IsInstanceOf($className); + } + + public static function isType(string $type): IsType + { + return new IsType($type); + } + + public static function lessThan($value): LessThan + { + return new LessThan($value); + } + + public static function lessThanOrEqual($value): LogicalOr + { + return static::logicalOr( + new IsEqual($value), + new LessThan($value) + ); + } + + public static function matchesRegularExpression(string $pattern): RegularExpression + { + return new RegularExpression($pattern); + } + + public static function matches(string $string): StringMatchesFormatDescription + { + return new StringMatchesFormatDescription($string); + } + + public static function stringStartsWith($prefix): StringStartsWith + { + return new StringStartsWith($prefix); + } + + public static function stringContains(string $string, bool $case = true): StringContains + { + return new StringContains($string, $case); + } + + public static function stringEndsWith(string $suffix): StringEndsWith + { + return new StringEndsWith($suffix); + } + + public static function countOf(int $count): Count + { + return new Count($count); + } + + /** + * Fails a test with the given message. + * + * @throws AssertionFailedError + */ + public static function fail(string $message = ''): void + { + self::$count++; + + throw new AssertionFailedError($message); + } + + /** + * Returns the value of an attribute of a class or an object. + * This also works for attributes that are declared protected or private. + * + * @param object|string $classOrObject + * + * @throws Exception + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function readAttribute($classOrObject, string $attributeName) + { + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(2, 'valid attribute name'); + } + + if (\is_string($classOrObject)) { + if (!\class_exists($classOrObject)) { + throw InvalidArgumentHelper::factory( + 1, + 'class name' + ); + } + + return static::getStaticAttribute( + $classOrObject, + $attributeName + ); + } + + if (\is_object($classOrObject)) { + return static::getObjectAttribute( + $classOrObject, + $attributeName + ); + } + + throw InvalidArgumentHelper::factory( + 1, + 'class name or object' + ); + } + + /** + * Returns the value of a static attribute. + * This also works for attributes that are declared protected or private. + * + * @throws Exception + * @throws ReflectionException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function getStaticAttribute(string $className, string $attributeName) + { + if (!\class_exists($className)) { + throw InvalidArgumentHelper::factory(1, 'class name'); + } + + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(2, 'valid attribute name'); + } + + $class = new ReflectionClass($className); + + while ($class) { + $attributes = $class->getStaticProperties(); + + if (\array_key_exists($attributeName, $attributes)) { + return $attributes[$attributeName]; + } + + $class = $class->getParentClass(); + } + + throw new Exception( + \sprintf( + 'Attribute "%s" not found in class.', + $attributeName + ) + ); + } + + /** + * Returns the value of an object's attribute. + * This also works for attributes that are declared protected or private. + * + * @param object $object + * + * @throws Exception + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 + */ + public static function getObjectAttribute($object, string $attributeName) + { + if (!\is_object($object)) { + throw InvalidArgumentHelper::factory(1, 'object'); + } + + if (!self::isValidAttributeName($attributeName)) { + throw InvalidArgumentHelper::factory(2, 'valid attribute name'); + } + + try { + $reflector = new ReflectionObject($object); + + do { + try { + $attribute = $reflector->getProperty($attributeName); + + if (!$attribute || $attribute->isPublic()) { + return $object->$attributeName; + } + + $attribute->setAccessible(true); + $value = $attribute->getValue($object); + $attribute->setAccessible(false); + + return $value; + } catch (ReflectionException $e) { + } + } while ($reflector = $reflector->getParentClass()); + } catch (ReflectionException $e) { + } + + throw new Exception( + \sprintf( + 'Attribute "%s" not found in object.', + $attributeName + ) + ); + } + + /** + * Mark the test as incomplete. + * + * @throws IncompleteTestError + */ + public static function markTestIncomplete(string $message = ''): void + { + throw new IncompleteTestError($message); + } + + /** + * Mark the test as skipped. + * + * @throws SkippedTestError + */ + public static function markTestSkipped(string $message = ''): void + { + throw new SkippedTestError($message); + } + + /** + * Return the current assertion count. + */ + public static function getCount(): int + { + return self::$count; + } + + /** + * Reset the assertion counter. + */ + public static function resetCount(): void + { + self::$count = 0; + } + + private static function isValidAttributeName(string $attributeName): bool + { + return \preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName); + } + + private static function createWarning(string $warning): void + { + foreach (\debug_backtrace() as $step) { + if (isset($step['object']) && $step['object'] instanceof TestCase) { + $step['object']->addWarning($warning); + + break; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Util\Filter; + +/** + * Base class for all PHPUnit Framework exceptions. + * + * Ensures that exceptions thrown during a test run do not leave stray + * references behind. + * + * Every Exception contains a stack trace. Each stack frame contains the 'args' + * of the called function. The function arguments can contain references to + * instantiated objects. The references prevent the objects from being + * destructed (until test results are eventually printed), so memory cannot be + * freed up. + * + * With enabled process isolation, test results are serialized in the child + * process and unserialized in the parent process. The stack trace of Exceptions + * may contain objects that cannot be serialized or unserialized (e.g., PDO + * connections). Unserializing user-space objects from the child process into + * the parent would break the intended encapsulation of process isolation. + * + * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions + */ +class Exception extends \RuntimeException implements \PHPUnit\Exception +{ + /** + * @var array + */ + protected $serializableTrace; + + public function __construct($message = '', $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->serializableTrace = $this->getTrace(); + + foreach ($this->serializableTrace as $i => $call) { + unset($this->serializableTrace[$i]['args']); + } + } + + /** + * @throws \InvalidArgumentException + */ + public function __toString(): string + { + $string = TestFailure::exceptionToString($this); + + if ($trace = Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; + } + + return $string; + } + + public function __sleep(): array + { + return \array_keys(\get_object_vars($this)); + } + + /** + * Returns the serializable trace (without 'args'). + */ + public function getSerializableTrace(): array + { + return $this->serializableTrace; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * A skipped test case + */ +class SkippedTestCase extends TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var bool + */ + protected $backupGlobals = false; + + /** + * @var bool + */ + protected $backupStaticAttributes = false; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * @var bool + */ + protected $useErrorHandler = false; + + /** + * @var bool + */ + protected $useOutputBuffering = false; + + public function __construct(string $className, string $methodName, string $message = '') + { + parent::__construct($className . '::' . $methodName); + + $this->message = $message; + } + + public function getMessage(): string + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return $this->getName(); + } + + /** + * @throws Exception + */ + protected function runTest(): void + { + $this->markTestSkipped($this->message); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Countable; + +/** + * A Test can be run and collect its results. + */ +interface Test extends Countable +{ + /** + * Runs a test and collects its result in a TestResult instance. + */ + public function run(TestResult $result = null): TestResult; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Util\Json; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Asserts whether or not two JSON objects are equal. + */ +class JsonMatches extends Constraint +{ + /** + * @var string + */ + private $value; + + public function __construct(string $value) + { + parent::__construct(); + + $this->value = $value; + } + + /** + * Returns a string representation of the object. + */ + public function toString(): string + { + return \sprintf( + 'matches JSON string "%s"', + $this->value + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + [$error, $recodedOther] = Json::canonicalize($other); + + if ($error) { + return false; + } + + [$error, $recodedValue] = Json::canonicalize($this->value); + + if ($error) { + return false; + } + + return $recodedOther == $recodedValue; + } + + /** + * Throws an exception for the given compared value and test description + * + * @param mixed $other evaluated value or object + * @param string $description Additional information about the test + * @param ComparisonFailure $comparisonFailure + * + * @throws ExpectationFailedException + * @throws \PHPUnit\Framework\Exception + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void + { + if ($comparisonFailure === null) { + [$error] = Json::canonicalize($other); + + if ($error) { + parent::fail($other, $description); + + return; + } + + [$error] = Json::canonicalize($this->value); + + if ($error) { + parent::fail($other, $description); + + return; + } + + $comparisonFailure = new ComparisonFailure( + \json_decode($this->value), + \json_decode($other), + Json::prettify($this->value), + Json::prettify($other), + false, + 'Failed asserting that two json values are equal.' + ); + } + + parent::fail($other, $description, $comparisonFailure); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the value it is evaluated for is greater + * than a given value. + */ +class GreaterThan extends Constraint +{ + /** + * @var float|int + */ + private $value; + + /** + * @param float|int $value + */ + public function __construct($value) + { + parent::__construct(); + + $this->value = $value; + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return 'is greater than ' . $this->exporter->export($this->value); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $this->value < $other; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that accepts finite. + */ +class IsFinite extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is finite'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \is_finite($other); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that checks if the file/dir(name) that it is evaluated for is readable. + * + * The file path to check is passed as $other in evaluate(). + */ +class IsReadable extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is readable'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \is_readable($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + '"%s" is readable', + $other + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Logical XOR. + */ +class LogicalXor extends Constraint +{ + /** + * @var Constraint[] + */ + private $constraints = []; + + public static function fromConstraints(Constraint ...$constraints): self + { + $constraint = new self; + + $constraint->constraints = \array_values($constraints); + + return $constraint; + } + + /** + * @param Constraint[] $constraints + */ + public function setConstraints(array $constraints): void + { + $this->constraints = []; + + foreach ($constraints as $constraint) { + if (!($constraint instanceof Constraint)) { + $constraint = new IsEqual( + $constraint + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = true; + $lastResult = null; + + foreach ($this->constraints as $constraint) { + $result = $constraint->evaluate($other, $description, true); + + if ($result === $lastResult) { + $success = false; + + break; + } + + $lastResult = $result; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' xor '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += \count($constraint); + } + + return $count; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use SebastianBergmann\Diff\Differ; + +/** + * ... + */ +class StringMatchesFormatDescription extends RegularExpression +{ + /** + * @var string + */ + private $string; + + public function __construct(string $string) + { + parent::__construct( + $this->createPatternFromFormat( + $this->convertNewlines($string) + ) + ); + + $this->string = $string; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return parent::matches( + $this->convertNewlines($other) + ); + } + + protected function failureDescription($other): string + { + return 'string matches format description'; + } + + protected function additionalFailureDescription($other): string + { + $from = \explode("\n", $this->string); + $to = \explode("\n", $this->convertNewlines($other)); + + foreach ($from as $index => $line) { + if (isset($to[$index]) && $line !== $to[$index]) { + $line = $this->createPatternFromFormat($line); + + if (\preg_match($line, $to[$index]) > 0) { + $from[$index] = $to[$index]; + } + } + } + + $this->string = \implode("\n", $from); + $other = \implode("\n", $to); + + $differ = new Differ("--- Expected\n+++ Actual\n"); + + return $differ->diff($this->string, $other); + } + + private function createPatternFromFormat(string $string): string + { + $string = \strtr( + \preg_quote($string, '/'), + [ + '%%' => '%', + '%e' => '\\' . \DIRECTORY_SEPARATOR, + '%s' => '[^\r\n]+', + '%S' => '[^\r\n]*', + '%a' => '.+', + '%A' => '.*', + '%w' => '\s*', + '%i' => '[+-]?\d+', + '%d' => '\d+', + '%x' => '[0-9a-fA-F]+', + '%f' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', + '%c' => '.', + ] + ); + + return '/^' . $string . '$/s'; + } + + private function convertNewlines($text): string + { + return \preg_replace('/\r\n/', "\n", $text); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that accepts infinite. + */ +class IsInfinite extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is infinite'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \is_infinite($other); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the value it is evaluated for is of a + * specified type. + * + * The expected value is passed in the constructor. + */ +class IsType extends Constraint +{ + public const TYPE_ARRAY = 'array'; + + public const TYPE_BOOL = 'bool'; + + public const TYPE_FLOAT = 'float'; + + public const TYPE_INT = 'int'; + + public const TYPE_NULL = 'null'; + + public const TYPE_NUMERIC = 'numeric'; + + public const TYPE_OBJECT = 'object'; + + public const TYPE_RESOURCE = 'resource'; + + public const TYPE_STRING = 'string'; + + public const TYPE_SCALAR = 'scalar'; + + public const TYPE_CALLABLE = 'callable'; + + public const TYPE_ITERABLE = 'iterable'; + + /** + * @var array + */ + private const KNOWN_TYPES = [ + 'array' => true, + 'boolean' => true, + 'bool' => true, + 'double' => true, + 'float' => true, + 'integer' => true, + 'int' => true, + 'null' => true, + 'numeric' => true, + 'object' => true, + 'real' => true, + 'resource' => true, + 'string' => true, + 'scalar' => true, + 'callable' => true, + 'iterable' => true, + ]; + + /** + * @var string + */ + private $type; + + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(string $type) + { + parent::__construct(); + + if (!isset(self::KNOWN_TYPES[$type])) { + throw new \PHPUnit\Framework\Exception( + \sprintf( + 'Type specified for PHPUnit\Framework\Constraint\IsType <%s> ' . + 'is not a valid type.', + $type + ) + ); + } + + $this->type = $type; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return \sprintf( + 'is of type "%s"', + $this->type + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + switch ($this->type) { + case 'numeric': + return \is_numeric($other); + + case 'integer': + case 'int': + return \is_int($other); + + case 'double': + case 'float': + case 'real': + return \is_float($other); + + case 'string': + return \is_string($other); + + case 'boolean': + case 'bool': + return \is_bool($other); + + case 'null': + return null === $other; + + case 'array': + return \is_array($other); + + case 'object': + return \is_object($other); + + case 'resource': + return \is_resource($other) || \is_string(@\get_resource_type($other)); + + case 'scalar': + return \is_scalar($other); + + case 'callable': + return \is_callable($other); + + case 'iterable': + return \is_iterable($other); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use ArrayAccess; + +/** + * Constraint that asserts that the array it is evaluated for has a given key. + * + * Uses array_key_exists() to check if the key is found in the input array, if + * not found the evaluation fails. + * + * The array key is passed in the constructor. + */ +class ArrayHasKey extends Constraint +{ + /** + * @var int|string + */ + private $key; + + /** + * @param int|string $key + */ + public function __construct($key) + { + parent::__construct(); + $this->key = $key; + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return 'has the key ' . $this->exporter->export($this->key); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + if (\is_array($other)) { + return \array_key_exists($this->key, $other); + } + + if ($other instanceof ArrayAccess) { + return $other->offsetExists($this->key); + } + + return false; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return 'an array ' . $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +class ExceptionCode extends Constraint +{ + /** + * @var int|string + */ + private $expectedCode; + + /** + * @param int|string $expected + */ + public function __construct($expected) + { + parent::__construct(); + + $this->expectedCode = $expected; + } + + public function toString(): string + { + return 'exception code is '; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param \Throwable $other + */ + protected function matches($other): bool + { + return (string) $other->getCode() === (string) $this->expectedCode; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return \sprintf( + '%s is equal to expected exception code %s', + $this->exporter->export($other->getCode()), + $this->exporter->export($this->expectedCode) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use ReflectionClass; +use ReflectionException; + +/** + * Constraint that asserts that the object it is evaluated for is an instance + * of a given class. + * + * The expected class name is passed in the constructor. + */ +class IsInstanceOf extends Constraint +{ + /** + * @var string + */ + private $className; + + public function __construct(string $className) + { + parent::__construct(); + + $this->className = $className; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return \sprintf( + 'is instance of %s "%s"', + $this->getType(), + $this->className + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $other instanceof $this->className; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return \sprintf( + '%s is an instance of %s "%s"', + $this->exporter->shortenedExport($other), + $this->getType(), + $this->className + ); + } + + private function getType(): string + { + try { + $reflection = new ReflectionClass($this->className); + + if ($reflection->isInterface()) { + return 'interface'; + } + } catch (ReflectionException $e) { + } + + return 'class'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +class SameSize extends Count +{ + public function __construct(iterable $expected) + { + parent::__construct($this->getCountOf($expected)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Constraint that asserts that the Traversable it is applied to contains + * only values of a given type. + */ +class TraversableContainsOnly extends Constraint +{ + /** + * @var Constraint + */ + private $constraint; + + /** + * @var string + */ + private $type; + + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(string $type, bool $isNativeType = true) + { + parent::__construct(); + + if ($isNativeType) { + $this->constraint = new IsType($type); + } else { + $this->constraint = new IsInstanceOf( + $type + ); + } + + $this->type = $type; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = true; + + foreach ($other as $item) { + if (!$this->constraint->evaluate($item, '', true)) { + $success = false; + + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'contains only values of type "' . $this->type . '"'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that evaluates against a specified closure. + */ +class Callback extends Constraint +{ + /** + * @var callable + */ + private $callback; + + public function __construct(callable $callback) + { + parent::__construct(); + + $this->callback = $callback; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is accepted by specified callback'; + } + + /** + * Evaluates the constraint for parameter $value. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \call_user_func($this->callback, $other); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +class ExceptionMessage extends Constraint +{ + /** + * @var string + */ + private $expectedMessage; + + public function __construct(string $expected) + { + parent::__construct(); + + $this->expectedMessage = $expected; + } + + public function toString(): string + { + if ($this->expectedMessage === '') { + return 'exception message is empty'; + } + + return 'exception message contains '; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param \Throwable $other + */ + protected function matches($other): bool + { + if ($this->expectedMessage === '') { + return $other->getMessage() === ''; + } + + return \strpos($other->getMessage(), $this->expectedMessage) !== false; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + if ($this->expectedMessage === '') { + return \sprintf( + "exception message is empty but is '%s'", + $other->getMessage() + ); + } + + return \sprintf( + "exception message '%s' contains '%s'", + $other->getMessage(), + $this->expectedMessage + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that checks if the directory(name) that it is evaluated for exists. + * + * The file path to check is passed as $other in evaluate(). + */ +class DirectoryExists extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'directory exists'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \is_dir($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + 'directory "%s" exists', + $other + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the value it is evaluated for is less than + * a given value. + */ +class LessThan extends Constraint +{ + /** + * @var float|int + */ + private $value; + + /** + * @param float|int $value + */ + public function __construct($value) + { + parent::__construct(); + + $this->value = $value; + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return 'is less than ' . $this->exporter->export($this->value); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $this->value > $other; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use ReflectionObject; + +/** + * Constraint that asserts that the object it is evaluated for has a given + * attribute. + * + * The attribute name is passed in the constructor. + */ +class ObjectHasAttribute extends ClassHasAttribute +{ + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + $object = new ReflectionObject($other); + + return $object->hasProperty($this->attributeName()); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the string it is evaluated for matches + * a regular expression. + * + * Checks a given value using the Perl Compatible Regular Expression extension + * in PHP. The pattern is matched by executing preg_match(). + * + * The pattern string passed in the constructor. + */ +class RegularExpression extends Constraint +{ + /** + * @var string + */ + private $pattern; + + public function __construct(string $pattern) + { + parent::__construct(); + + $this->pattern = $pattern; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return \sprintf( + 'matches PCRE pattern "%s"', + $this->pattern + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \preg_match($this->pattern, $other) > 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Util\Filter; +use Throwable; + +class Exception extends Constraint +{ + /** + * @var string + */ + private $className; + + public function __construct(string $className) + { + parent::__construct(); + + $this->className = $className; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return \sprintf( + 'exception of type "%s"', + $this->className + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $other instanceof $this->className; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + if ($other !== null) { + $message = ''; + + if ($other instanceof Throwable) { + $message = '. Message was: "' . $other->getMessage() . '" at' + . "\n" . Filter::getFilteredStacktrace($other); + } + + return \sprintf( + 'exception of type "%s" matches expected exception "%s"%s', + \get_class($other), + $this->className, + $message + ); + } + + return \sprintf( + 'exception of type "%s" is thrown', + $this->className + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use SplObjectStorage; + +/** + * Constraint that asserts that the Traversable it is applied to contains + * a given value. + */ +class TraversableContains extends Constraint +{ + /** + * @var bool + */ + private $checkForObjectIdentity; + + /** + * @var bool + */ + private $checkForNonObjectIdentity; + + /** + * @var mixed + */ + private $value; + + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false) + { + parent::__construct(); + + $this->checkForObjectIdentity = $checkForObjectIdentity; + $this->checkForNonObjectIdentity = $checkForNonObjectIdentity; + $this->value = $value; + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + if (\is_string($this->value) && \strpos($this->value, "\n") !== false) { + return 'contains "' . $this->value . '"'; + } + + return 'contains ' . $this->exporter->export($this->value); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + if ($other instanceof SplObjectStorage) { + return $other->contains($this->value); + } + + if (\is_object($this->value)) { + foreach ($other as $element) { + if ($this->checkForObjectIdentity && $element === $this->value) { + return true; + } + + if (!$this->checkForObjectIdentity && $element == $this->value) { + return true; + } + } + } else { + foreach ($other as $element) { + if ($this->checkForNonObjectIdentity && $element === $this->value) { + return true; + } + + if (!$this->checkForNonObjectIdentity && $element == $this->value) { + return true; + } + } + } + + return false; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return \sprintf( + '%s %s', + \is_array($other) ? 'an array' : 'a traversable', + $this->toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the string it is evaluated for ends with a given + * suffix. + */ +class StringEndsWith extends Constraint +{ + /** + * @var string + */ + private $suffix; + + public function __construct(string $suffix) + { + parent::__construct(); + + $this->suffix = $suffix; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'ends with "' . $this->suffix . '"'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \substr($other, 0 - \strlen($this->suffix)) === $this->suffix; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use SebastianBergmann\Comparator\ComparisonFailure; +use SebastianBergmann\Comparator\Factory as ComparatorFactory; + +/** + * Constraint that checks if one value is equal to another. + * + * Equality is checked with PHP's == operator, the operator is explained in + * detail at {@url https://php.net/manual/en/types.comparisons.php}. + * Two values are equal if they have the same value disregarding type. + * + * The expected value is passed in the constructor. + */ +class IsEqual extends Constraint +{ + /** + * @var mixed + */ + private $value; + + /** + * @var float + */ + private $delta; + + /** + * @var int + */ + private $maxDepth; + + /** + * @var bool + */ + private $canonicalize; + + /** + * @var bool + */ + private $ignoreCase; + + public function __construct($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false) + { + parent::__construct(); + + $this->value = $value; + $this->delta = $delta; + $this->maxDepth = $maxDepth; + $this->canonicalize = $canonicalize; + $this->ignoreCase = $ignoreCase; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + // If $this->value and $other are identical, they are also equal. + // This is the most common path and will allow us to skip + // initialization of all the comparators. + if ($this->value === $other) { + return true; + } + + $comparatorFactory = ComparatorFactory::getInstance(); + + try { + $comparator = $comparatorFactory->getComparatorFor( + $this->value, + $other + ); + + $comparator->assertEquals( + $this->value, + $other, + $this->delta, + $this->canonicalize, + $this->ignoreCase + ); + } catch (ComparisonFailure $f) { + if ($returnResult) { + return false; + } + + throw new ExpectationFailedException( + \trim($description . "\n" . $f->getMessage()), + $f + ); + } + + return true; + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + $delta = ''; + + if (\is_string($this->value)) { + if (\strpos($this->value, "\n") !== false) { + return 'is equal to '; + } + + return \sprintf( + "is equal to '%s'", + $this->value + ); + } + + if ($this->delta != 0) { + $delta = \sprintf( + ' with delta <%F>', + $this->delta + ); + } + + return \sprintf( + 'is equal to %s%s', + $this->exporter->export($this->value), + $delta + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Constraint that asserts that the array it is evaluated for has a specified subset. + * + * Uses array_replace_recursive() to check if a key value subset is part of the + * subject array. + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494 + */ +class ArraySubset extends Constraint +{ + /** + * @var iterable + */ + private $subset; + + /** + * @var bool + */ + private $strict; + + public function __construct(iterable $subset, bool $strict = false) + { + parent::__construct(); + + $this->strict = $strict; + $this->subset = $subset; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + //type cast $other & $this->subset as an array to allow + //support in standard array functions. + $other = $this->toArray($other); + $this->subset = $this->toArray($this->subset); + + $patched = \array_replace_recursive($other, $this->subset); + + if ($this->strict) { + $result = $other === $patched; + } else { + $result = $other == $patched; + } + + if ($returnResult) { + return $result; + } + + if (!$result) { + $f = new ComparisonFailure( + $patched, + $other, + \var_export($patched, true), + \var_export($other, true) + ); + + $this->fail($other, $description, $f); + } + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return 'has the subset ' . $this->exporter->export($this->subset); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return 'an array ' . $this->toString(); + } + + private function toArray(iterable $other): array + { + if (\is_array($other)) { + return $other; + } + + if ($other instanceof \ArrayObject) { + return $other->getArrayCopy(); + } + + if ($other instanceof \Traversable) { + return \iterator_to_array($other); + } + + // Keep BC even if we know that array would not be the expected one + return (array) $other; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\ExpectationFailedException; + +class Attribute extends Composite +{ + /** + * @var string + */ + private $attributeName; + + public function __construct(Constraint $constraint, string $attributeName) + { + parent::__construct($constraint); + + $this->attributeName = $attributeName; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \PHPUnit\Framework\Exception + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + return parent::evaluate( + Assert::readAttribute( + $other, + $this->attributeName + ), + $description, + $returnResult + ); + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'attribute "' . $this->attributeName . '" ' . $this->innerConstraint()->toString(); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that checks if the file/dir(name) that it is evaluated for is writable. + * + * The file path to check is passed as $other in evaluate(). + */ +class IsWritable extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is writable'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \is_writable($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + '"%s" is writable', + $other + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use Countable; + +/** + * Constraint that checks whether a variable is empty(). + */ +class IsEmpty extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is empty'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + if ($other instanceof Countable) { + return \count($other) === 0; + } + + return empty($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + $type = \gettype($other); + + return \sprintf( + '%s %s %s', + $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a', + $type, + $this->toString() + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the string it is evaluated for begins with a + * given prefix. + */ +class StringStartsWith extends Constraint +{ + /** + * @var string + */ + private $prefix; + + public function __construct(string $prefix) + { + parent::__construct(); + + $this->prefix = $prefix; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'starts with "' . $this->prefix . '"'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \strpos($other, $this->prefix) === 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that accepts false. + */ +class IsFalse extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is false'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $other === false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +abstract class Composite extends Constraint +{ + /** + * @var Constraint + */ + private $innerConstraint; + + public function __construct(Constraint $innerConstraint) + { + parent::__construct(); + + $this->innerConstraint = $innerConstraint; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + try { + return $this->innerConstraint->evaluate( + $other, + $description, + $returnResult + ); + } catch (ExpectationFailedException $e) { + $this->fail($other, $description, $e->getComparisonFailure()); + } + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + return \count($this->innerConstraint); + } + + protected function innerConstraint(): Constraint + { + return $this->innerConstraint; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use Countable; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\SelfDescribing; +use SebastianBergmann\Comparator\ComparisonFailure; +use SebastianBergmann\Exporter\Exporter; + +/** + * Abstract base class for constraints which can be applied to any value. + */ +abstract class Constraint implements Countable, SelfDescribing +{ + protected $exporter; + + public function __construct() + { + $this->exporter = new Exporter; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = false; + + if ($this->matches($other)) { + $success = true; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + return 1; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * This method can be overridden to implement the evaluation algorithm. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return false; + } + + /** + * Throws an exception for the given compared value and test description + * + * @param mixed $other evaluated value or object + * @param string $description Additional information about the test + * @param ComparisonFailure $comparisonFailure + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void + { + $failureDescription = \sprintf( + 'Failed asserting that %s.', + $this->failureDescription($other) + ); + + $additionalFailureDescription = $this->additionalFailureDescription($other); + + if ($additionalFailureDescription) { + $failureDescription .= "\n" . $additionalFailureDescription; + } + + if (!empty($description)) { + $failureDescription = $description . "\n" . $failureDescription; + } + + throw new ExpectationFailedException( + $failureDescription, + $comparisonFailure + ); + } + + /** + * Return additional failure description where needed + * + * The function can be overridden to provide additional failure + * information like a diff + * + * @param mixed $other evaluated value or object + */ + protected function additionalFailureDescription($other): string + { + return ''; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * To provide additional failure information additionalFailureDescription + * can be used. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + return $this->exporter->export($other) . ' ' . $this->toString(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Logical OR. + */ +class LogicalOr extends Constraint +{ + /** + * @var Constraint[] + */ + private $constraints = []; + + public static function fromConstraints(Constraint ...$constraints): self + { + $constraint = new self; + + $constraint->constraints = \array_values($constraints); + + return $constraint; + } + + /** + * @param Constraint[] $constraints + */ + public function setConstraints(array $constraints): void + { + $this->constraints = []; + + foreach ($constraints as $constraint) { + if (!($constraint instanceof Constraint)) { + $constraint = new IsEqual( + $constraint + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = false; + + foreach ($this->constraints as $constraint) { + if ($constraint->evaluate($other, $description, true)) { + $success = true; + + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' or '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += \count($constraint); + } + + return $count; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Util\RegularExpression as RegularExpressionUtil; + +class ExceptionMessageRegularExpression extends Constraint +{ + /** + * @var string + */ + private $expectedMessageRegExp; + + public function __construct(string $expected) + { + parent::__construct(); + + $this->expectedMessageRegExp = $expected; + } + + public function toString(): string + { + return 'exception message matches '; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param \PHPUnit\Framework\Exception $other + * + * @throws \Exception + * @throws \PHPUnit\Framework\Exception + */ + protected function matches($other): bool + { + $match = RegularExpressionUtil::safeMatch($this->expectedMessageRegExp, $other->getMessage()); + + if ($match === false) { + throw new \PHPUnit\Framework\Exception( + "Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'" + ); + } + + return $match === 1; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + "exception message '%s' matches '%s'", + $other->getMessage(), + $this->expectedMessageRegExp + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that accepts nan. + */ +class IsNan extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is nan'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \is_nan($other); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Constraint that asserts that one value is identical to another. + * + * Identical check is performed with PHP's === operator, the operator is + * explained in detail at + * {@url https://php.net/manual/en/types.comparisons.php}. + * Two values are identical if they have the same value and are of the same + * type. + * + * The expected value is passed in the constructor. + */ +class IsIdentical extends Constraint +{ + /** + * @var float + */ + private const EPSILON = 0.0000000001; + + /** + * @var mixed + */ + private $value; + + public function __construct($value) + { + parent::__construct(); + + $this->value = $value; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + if (\is_float($this->value) && \is_float($other) && + !\is_infinite($this->value) && !\is_infinite($other) && + !\is_nan($this->value) && !\is_nan($other)) { + $success = \abs($this->value - $other) < self::EPSILON; + } else { + $success = $this->value === $other; + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $f = null; + + // if both values are strings, make sure a diff is generated + if (\is_string($this->value) && \is_string($other)) { + $f = new ComparisonFailure( + $this->value, + $other, + \sprintf("'%s'", $this->value), + \sprintf("'%s'", $other) + ); + } + + // if both values are array, make sure a diff is generated + if (\is_array($this->value) && \is_array($other)) { + $f = new ComparisonFailure( + $this->value, + $other, + $this->exporter->export($this->value), + $this->exporter->export($other) + ); + } + + $this->fail($other, $description, $f); + } + } + + /** + * Returns a string representation of the constraint. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + if (\is_object($this->value)) { + return 'is identical to an object of class "' . + \get_class($this->value) . '"'; + } + + return 'is identical to ' . $this->exporter->export($this->value); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + if (\is_object($this->value) && \is_object($other)) { + return 'two variables reference the same object'; + } + + if (\is_string($this->value) && \is_string($other)) { + return 'two strings are identical'; + } + + if (\is_array($this->value) && \is_array($other)) { + return 'two arrays are identical'; + } + + return parent::failureDescription($other); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use ReflectionClass; + +/** + * Constraint that asserts that the class it is evaluated for has a given + * static attribute. + * + * The attribute name is passed in the constructor. + */ +class ClassHasStaticAttribute extends ClassHasAttribute +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return \sprintf( + 'has static attribute "%s"', + $this->attributeName() + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + $class = new ReflectionClass($other); + + if ($class->hasProperty($this->attributeName())) { + $attribute = $class->getProperty($this->attributeName()); + + return $attribute->isStatic(); + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use Countable; +use Generator; +use Iterator; +use IteratorAggregate; +use Traversable; + +class Count extends Constraint +{ + /** + * @var int + */ + private $expectedCount; + + public function __construct(int $expected) + { + parent::__construct(); + + $this->expectedCount = $expected; + } + + public function toString(): string + { + return \sprintf( + 'count matches %d', + $this->expectedCount + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + */ + protected function matches($other): bool + { + return $this->expectedCount === $this->getCountOf($other); + } + + /** + * @param iterable $other + */ + protected function getCountOf($other): ?int + { + if ($other instanceof Countable || \is_array($other)) { + return \count($other); + } + + if ($other instanceof Traversable) { + while ($other instanceof IteratorAggregate) { + $other = $other->getIterator(); + } + + $iterator = $other; + + if ($iterator instanceof Generator) { + return $this->getCountOfGenerator($iterator); + } + + if (!$iterator instanceof Iterator) { + return \iterator_count($iterator); + } + + $key = $iterator->key(); + $count = \iterator_count($iterator); + + // Manually rewind $iterator to previous key, since iterator_count + // moves pointer. + if ($key !== null) { + $iterator->rewind(); + + while ($iterator->valid() && $key !== $iterator->key()) { + $iterator->next(); + } + } + + return $count; + } + } + + /** + * Returns the total number of iterations from a generator. + * This will fully exhaust the generator. + */ + protected function getCountOfGenerator(Generator $generator): int + { + for ($count = 0; $generator->valid(); $generator->next()) { + ++$count; + } + + return $count; + } + + /** + * Returns the description of the failure. + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + 'actual size %d matches expected size %d', + $this->getCountOf($other), + $this->expectedCount + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Constraint that accepts any input value. + */ +class IsAnything extends Constraint +{ + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + return $returnResult ? true : null; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is anything'; + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + return 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that accepts null. + */ +class IsNull extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is null'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $other === null; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use ReflectionClass; + +/** + * Constraint that asserts that the class it is evaluated for has a given + * attribute. + * + * The attribute name is passed in the constructor. + */ +class ClassHasAttribute extends Constraint +{ + /** + * @var string + */ + private $attributeName; + + public function __construct(string $attributeName) + { + parent::__construct(); + + $this->attributeName = $attributeName; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return \sprintf( + 'has attribute "%s"', + $this->attributeName + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + $class = new ReflectionClass($other); + + return $class->hasProperty($this->attributeName); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + '%sclass "%s" %s', + \is_object($other) ? 'object of ' : '', + \is_object($other) ? \get_class($other) : $other, + $this->toString() + ); + } + + protected function attributeName(): string + { + return $this->attributeName; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Logical NOT. + */ +class LogicalNot extends Constraint +{ + /** + * @var Constraint + */ + private $constraint; + + public static function negate(string $string): string + { + $positives = [ + 'contains ', + 'exists', + 'has ', + 'is ', + 'are ', + 'matches ', + 'starts with ', + 'ends with ', + 'reference ', + 'not not ', + ]; + + $negatives = [ + 'does not contain ', + 'does not exist', + 'does not have ', + 'is not ', + 'are not ', + 'does not match ', + 'starts not with ', + 'ends not with ', + 'don\'t reference ', + 'not ', + ]; + + \preg_match('/(\'[\w\W]*\')([\w\W]*)("[\w\W]*")/i', $string, $matches); + + if (\count($matches) > 0) { + $nonInput = $matches[2]; + + $negatedString = \str_replace( + $nonInput, + \str_replace( + $positives, + $negatives, + $nonInput + ), + $string + ); + } else { + $negatedString = \str_replace( + $positives, + $negatives, + $string + ); + } + + return $negatedString; + } + + /** + * @param Constraint|mixed $constraint + */ + public function __construct($constraint) + { + parent::__construct(); + + if (!($constraint instanceof Constraint)) { + $constraint = new IsEqual($constraint); + } + + $this->constraint = $constraint; + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = !$this->constraint->evaluate($other, $description, true); + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + switch (\get_class($this->constraint)) { + case LogicalAnd::class: + case self::class: + case LogicalOr::class: + return 'not( ' . $this->constraint->toString() . ' )'; + + default: + return self::negate( + $this->constraint->toString() + ); + } + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + return \count($this->constraint); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + switch (\get_class($this->constraint)) { + case LogicalAnd::class: + case self::class: + case LogicalOr::class: + return 'not( ' . $this->constraint->failureDescription($other) . ' )'; + + default: + return self::negate( + $this->constraint->failureDescription($other) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Provides human readable messages for each JSON error. + */ +class JsonMatchesErrorMessageProvider +{ + /** + * Translates JSON error to a human readable string. + */ + public static function determineJsonError(string $error, string $prefix = ''): ?string + { + switch ($error) { + case \JSON_ERROR_NONE: + return null; + case \JSON_ERROR_DEPTH: + return $prefix . 'Maximum stack depth exceeded'; + case \JSON_ERROR_STATE_MISMATCH: + return $prefix . 'Underflow or the modes mismatch'; + case \JSON_ERROR_CTRL_CHAR: + return $prefix . 'Unexpected control character found'; + case \JSON_ERROR_SYNTAX: + return $prefix . 'Syntax error, malformed JSON'; + case \JSON_ERROR_UTF8: + return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded'; + default: + return $prefix . 'Unknown error'; + } + } + + /** + * Translates a given type to a human readable message prefix. + */ + public static function translateTypeToPrefix(string $type): string + { + switch (\strtolower($type)) { + case 'expected': + $prefix = 'Expected value JSON decode error - '; + + break; + case 'actual': + $prefix = 'Actual value JSON decode error - '; + + break; + default: + $prefix = ''; + + break; + } + + return $prefix; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that accepts true. + */ +class IsTrue extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is true'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return $other === true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that the string it is evaluated for contains + * a given string. + * + * Uses mb_strpos() to find the position of the string in the input, if not + * found the evaluation fails. + * + * The sub-string is passed in the constructor. + */ +class StringContains extends Constraint +{ + /** + * @var string + */ + private $string; + + /** + * @var bool + */ + private $ignoreCase; + + public function __construct(string $string, bool $ignoreCase = false) + { + parent::__construct(); + + $this->string = $string; + $this->ignoreCase = $ignoreCase; + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + if ($this->ignoreCase) { + $string = \mb_strtolower($this->string); + } else { + $string = $this->string; + } + + return \sprintf( + 'contains "%s"', + $string + ); + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + if ('' === $this->string) { + return true; + } + + if ($this->ignoreCase) { + return \mb_stripos($other, $this->string) !== false; + } + + return \mb_strpos($other, $this->string) !== false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that asserts that a string is valid JSON. + */ +class IsJson extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'is valid JSON'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + if ($other === '') { + return false; + } + + \json_decode($other); + + if (\json_last_error()) { + return false; + } + + return true; + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + protected function failureDescription($other): string + { + if ($other === '') { + return 'an empty string is valid JSON'; + } + + \json_decode($other); + $error = JsonMatchesErrorMessageProvider::determineJsonError( + \json_last_error() + ); + + return \sprintf( + '%s is valid JSON (%s)', + $this->exporter->shortenedExport($other), + $error + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +/** + * Constraint that checks if the file(name) that it is evaluated for exists. + * + * The file path to check is passed as $other in evaluate(). + */ +class FileExists extends Constraint +{ + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + return 'file exists'; + } + + /** + * Evaluates the constraint for parameter $other. Returns true if the + * constraint is met, false otherwise. + * + * @param mixed $other value or object to evaluate + */ + protected function matches($other): bool + { + return \file_exists($other); + } + + /** + * Returns the description of the failure + * + * The beginning of failure messages is "Failed asserting that" in most + * cases. This method should return the second part of that sentence. + * + * @param mixed $other evaluated value or object + */ + protected function failureDescription($other): string + { + return \sprintf( + 'file "%s" exists', + $other + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\Constraint; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Logical AND. + */ +class LogicalAnd extends Constraint +{ + /** + * @var Constraint[] + */ + private $constraints = []; + + public static function fromConstraints(Constraint ...$constraints): self + { + $constraint = new self; + + $constraint->constraints = \array_values($constraints); + + return $constraint; + } + + /** + * @param Constraint[] $constraints + * + * @throws \PHPUnit\Framework\Exception + */ + public function setConstraints(array $constraints): void + { + $this->constraints = []; + + foreach ($constraints as $constraint) { + if (!($constraint instanceof Constraint)) { + throw new \PHPUnit\Framework\Exception( + 'All parameters to ' . __CLASS__ . + ' must be a constraint object.' + ); + } + + $this->constraints[] = $constraint; + } + } + + /** + * Evaluates the constraint for parameter $other + * + * If $returnResult is set to false (the default), an exception is thrown + * in case of a failure. null is returned otherwise. + * + * If $returnResult is true, the result of the evaluation is returned as + * a boolean value instead: true in case of success, false in case of a + * failure. + * + * @param mixed $other value or object to evaluate + * @param string $description Additional information about the test + * @param bool $returnResult Whether to return a result or throw an exception + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function evaluate($other, $description = '', $returnResult = false) + { + $success = true; + + foreach ($this->constraints as $constraint) { + if (!$constraint->evaluate($other, $description, true)) { + $success = false; + + break; + } + } + + if ($returnResult) { + return $success; + } + + if (!$success) { + $this->fail($other, $description); + } + } + + /** + * Returns a string representation of the constraint. + */ + public function toString(): string + { + $text = ''; + + foreach ($this->constraints as $key => $constraint) { + if ($key > 0) { + $text .= ' and '; + } + + $text .= $constraint->toString(); + } + + return $text; + } + + /** + * Counts the number of constraint elements. + */ + public function count(): int + { + $count = 0; + + foreach ($this->constraints as $constraint) { + $count += \count($constraint); + } + + return $count; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use DeepCopy\DeepCopy; +use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint; +use PHPUnit\Framework\Constraint\ExceptionCode; +use PHPUnit\Framework\Constraint\ExceptionMessage; +use PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression; +use PHPUnit\Framework\MockObject\Generator as MockGenerator; +use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount as AnyInvokedCountMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtIndex as InvokedAtIndexMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastCount as InvokedAtLeastCountMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtMostCount as InvokedAtMostCountMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; +use PHPUnit\Framework\MockObject\MockBuilder; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; +use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; +use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; +use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; +use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; +use PHPUnit\Framework\MockObject\Stub\ReturnStub; +use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\GlobalState; +use PHPUnit\Util\PHP\AbstractPhpProcess; +use Prophecy; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophet; +use ReflectionClass; +use ReflectionException; +use ReflectionObject; +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Comparator\Factory as ComparatorFactory; +use SebastianBergmann\Diff\Differ; +use SebastianBergmann\Exporter\Exporter; +use SebastianBergmann\GlobalState\Blacklist; +use SebastianBergmann\GlobalState\Restorer; +use SebastianBergmann\GlobalState\Snapshot; +use SebastianBergmann\ObjectEnumerator\Enumerator; +use Text_Template; +use Throwable; + +abstract class TestCase extends Assert implements Test, SelfDescribing +{ + private const LOCALE_CATEGORIES = [\LC_ALL, \LC_COLLATE, \LC_CTYPE, \LC_MONETARY, \LC_NUMERIC, \LC_TIME]; + + /** + * @var bool + */ + protected $backupGlobals; + + /** + * @var array + */ + protected $backupGlobalsBlacklist = []; + + /** + * @var bool + */ + protected $backupStaticAttributes; + + /** + * @var array + */ + protected $backupStaticAttributesBlacklist = []; + + /** + * @var bool + */ + protected $runTestInSeparateProcess; + + /** + * @var bool + */ + protected $preserveGlobalState = true; + + /** + * @var bool + */ + private $runClassInSeparateProcess; + + /** + * @var bool + */ + private $inIsolation = false; + + /** + * @var array + */ + private $data; + + /** + * @var string + */ + private $dataName; + + /** + * @var bool + */ + private $useErrorHandler; + + /** + * @var null|string + */ + private $expectedException; + + /** + * @var string + */ + private $expectedExceptionMessage; + + /** + * @var string + */ + private $expectedExceptionMessageRegExp; + + /** + * @var null|int|string + */ + private $expectedExceptionCode; + + /** + * @var string + */ + private $name; + + /** + * @var string[] + */ + private $dependencies = []; + + /** + * @var array + */ + private $dependencyInput = []; + + /** + * @var array + */ + private $iniSettings = []; + + /** + * @var array + */ + private $locale = []; + + /** + * @var array + */ + private $mockObjects = []; + + /** + * @var MockGenerator + */ + private $mockObjectGenerator; + + /** + * @var int + */ + private $status = BaseTestRunner::STATUS_UNKNOWN; + + /** + * @var string + */ + private $statusMessage = ''; + + /** + * @var int + */ + private $numAssertions = 0; + + /** + * @var TestResult + */ + private $result; + + /** + * @var mixed + */ + private $testResult; + + /** + * @var string + */ + private $output = ''; + + /** + * @var string + */ + private $outputExpectedRegex; + + /** + * @var string + */ + private $outputExpectedString; + + /** + * @var mixed + */ + private $outputCallback = false; + + /** + * @var bool + */ + private $outputBufferingActive = false; + + /** + * @var int + */ + private $outputBufferingLevel; + + /** + * @var Snapshot + */ + private $snapshot; + + /** + * @var Prophecy\Prophet + */ + private $prophet; + + /** + * @var bool + */ + private $beStrictAboutChangesToGlobalState = false; + + /** + * @var bool + */ + private $registerMockObjectsFromTestArgumentsRecursively = false; + + /** + * @var string[] + */ + private $warnings = []; + + /** + * @var array + */ + private $groups = []; + + /** + * @var bool + */ + private $doesNotPerformAssertions = false; + + /** + * @var Comparator[] + */ + private $customComparators = []; + + /** + * Returns a matcher that matches when the method is executed + * zero or more times. + */ + public static function any(): AnyInvokedCountMatcher + { + return new AnyInvokedCountMatcher; + } + + /** + * Returns a matcher that matches when the method is never executed. + */ + public static function never(): InvokedCountMatcher + { + return new InvokedCountMatcher(0); + } + + /** + * Returns a matcher that matches when the method is executed + * at least N times. + */ + public static function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher + { + return new InvokedAtLeastCountMatcher( + $requiredInvocations + ); + } + + /** + * Returns a matcher that matches when the method is executed at least once. + */ + public static function atLeastOnce(): InvokedAtLeastOnceMatcher + { + return new InvokedAtLeastOnceMatcher; + } + + /** + * Returns a matcher that matches when the method is executed exactly once. + */ + public static function once(): InvokedCountMatcher + { + return new InvokedCountMatcher(1); + } + + /** + * Returns a matcher that matches when the method is executed + * exactly $count times. + */ + public static function exactly(int $count): InvokedCountMatcher + { + return new InvokedCountMatcher($count); + } + + /** + * Returns a matcher that matches when the method is executed + * at most N times. + */ + public static function atMost(int $allowedInvocations): InvokedAtMostCountMatcher + { + return new InvokedAtMostCountMatcher($allowedInvocations); + } + + /** + * Returns a matcher that matches when the method is executed + * at the given index. + */ + public static function at(int $index): InvokedAtIndexMatcher + { + return new InvokedAtIndexMatcher($index); + } + + public static function returnValue($value): ReturnStub + { + return new ReturnStub($value); + } + + public static function returnValueMap(array $valueMap): ReturnValueMapStub + { + return new ReturnValueMapStub($valueMap); + } + + public static function returnArgument(int $argumentIndex): ReturnArgumentStub + { + return new ReturnArgumentStub($argumentIndex); + } + + public static function returnCallback($callback): ReturnCallbackStub + { + return new ReturnCallbackStub($callback); + } + + /** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + */ + public static function returnSelf(): ReturnSelfStub + { + return new ReturnSelfStub; + } + + public static function throwException(Throwable $exception): ExceptionStub + { + return new ExceptionStub($exception); + } + + public static function onConsecutiveCalls(...$args): ConsecutiveCallsStub + { + return new ConsecutiveCallsStub($args); + } + + /** + * @param string $name + * @param string $dataName + */ + public function __construct($name = null, array $data = [], $dataName = '') + { + if ($name !== null) { + $this->setName($name); + } + + $this->data = $data; + $this->dataName = $dataName; + } + + /** + * This method is called before the first test of this test class is run. + */ + public static function setUpBeforeClass()/* The :void return type declaration that should be here would cause a BC issue */ + { + } + + /** + * This method is called after the last test of this test class is run. + */ + public static function tearDownAfterClass()/* The :void return type declaration that should be here would cause a BC issue */ + { + } + + /** + * This method is called before each test. + */ + protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */ + { + } + + /** + * This method is called after each test. + */ + protected function tearDown()/* The :void return type declaration that should be here would cause a BC issue */ + { + } + + /** + * Returns a string representation of the test case. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \ReflectionException + */ + public function toString(): string + { + $class = new ReflectionClass($this); + + $buffer = \sprintf( + '%s::%s', + $class->name, + $this->getName(false) + ); + + return $buffer . $this->getDataSetAsString(); + } + + public function count(): int + { + return 1; + } + + public function getGroups(): array + { + return $this->groups; + } + + public function setGroups(array $groups): void + { + $this->groups = $groups; + } + + public function getAnnotations(): array + { + return \PHPUnit\Util\Test::parseTestMethodAnnotations( + \get_class($this), + $this->name + ); + } + + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function getName(bool $withDataSet = true): ?string + { + if ($withDataSet) { + return $this->name . $this->getDataSetAsString(false); + } + + return $this->name; + } + + /** + * Returns the size of the test. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function getSize(): int + { + return \PHPUnit\Util\Test::getSize( + \get_class($this), + $this->getName(false) + ); + } + + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function hasSize(): bool + { + return $this->getSize() !== \PHPUnit\Util\Test::UNKNOWN; + } + + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function isSmall(): bool + { + return $this->getSize() === \PHPUnit\Util\Test::SMALL; + } + + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function isMedium(): bool + { + return $this->getSize() === \PHPUnit\Util\Test::MEDIUM; + } + + /** + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function isLarge(): bool + { + return $this->getSize() === \PHPUnit\Util\Test::LARGE; + } + + public function getActualOutput(): string + { + if (!$this->outputBufferingActive) { + return $this->output; + } + + return \ob_get_contents(); + } + + public function hasOutput(): bool + { + if ($this->output === '') { + return false; + } + + if ($this->hasExpectationOnOutput()) { + return false; + } + + return true; + } + + public function doesNotPerformAssertions(): bool + { + return $this->doesNotPerformAssertions; + } + + public function expectOutputRegex(string $expectedRegex): void + { + $this->outputExpectedRegex = $expectedRegex; + } + + public function expectOutputString(string $expectedString): void + { + $this->outputExpectedString = $expectedString; + } + + public function hasExpectationOnOutput(): bool + { + return \is_string($this->outputExpectedString) || \is_string($this->outputExpectedRegex); + } + + public function getExpectedException(): ?string + { + return $this->expectedException; + } + + /** + * @return null|int|string + */ + public function getExpectedExceptionCode() + { + return $this->expectedExceptionCode; + } + + public function getExpectedExceptionMessage(): string + { + return $this->expectedExceptionMessage; + } + + public function getExpectedExceptionMessageRegExp(): string + { + return $this->expectedExceptionMessageRegExp; + } + + public function expectException(string $exception): void + { + $this->expectedException = $exception; + } + + /** + * @param int|string $code + */ + public function expectExceptionCode($code): void + { + $this->expectedExceptionCode = $code; + } + + public function expectExceptionMessage(string $message): void + { + $this->expectedExceptionMessage = $message; + } + + public function expectExceptionMessageRegExp(string $messageRegExp): void + { + $this->expectedExceptionMessageRegExp = $messageRegExp; + } + + /** + * Sets up an expectation for an exception to be raised by the code under test. + * Information for expected exception class, expected exception message, and + * expected exception code are retrieved from a given Exception object. + */ + public function expectExceptionObject(\Exception $exception): void + { + $this->expectException(\get_class($exception)); + $this->expectExceptionMessage($exception->getMessage()); + $this->expectExceptionCode($exception->getCode()); + } + + public function expectNotToPerformAssertions() + { + $this->doesNotPerformAssertions = true; + } + + public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void + { + $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + } + + public function setUseErrorHandler(bool $useErrorHandler): void + { + $this->useErrorHandler = $useErrorHandler; + } + + public function getStatus(): int + { + return $this->status; + } + + public function markAsRisky(): void + { + $this->status = BaseTestRunner::STATUS_RISKY; + } + + public function getStatusMessage(): string + { + return $this->statusMessage; + } + + public function hasFailed(): bool + { + $status = $this->getStatus(); + + return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR; + } + + /** + * Runs the test case and collects the results in a TestResult object. + * If no TestResult object is passed a new one will be created. + * + * @throws CodeCoverageException + * @throws ReflectionException + * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException + * @throws \SebastianBergmann\CodeCoverage\RuntimeException + * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function run(TestResult $result = null): TestResult + { + if ($result === null) { + $result = $this->createResult(); + } + + if (!$this instanceof WarningTestCase) { + $this->setTestResultObject($result); + $this->setUseErrorHandlerFromAnnotation(); + } + + if ($this->useErrorHandler !== null) { + $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions(); + $result->convertErrorsToExceptions($this->useErrorHandler); + } + + if (!$this instanceof WarningTestCase && + !$this instanceof SkippedTestCase && + !$this->handleDependencies()) { + return $result; + } + + if ($this->runInSeparateProcess()) { + $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; + + $class = new ReflectionClass($this); + + if ($runEntireClass) { + $template = new Text_Template( + __DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl' + ); + } else { + $template = new Text_Template( + __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl' + ); + } + + if ($this->preserveGlobalState) { + $constants = GlobalState::getConstantsAsString(); + $globals = GlobalState::getGlobalsAsString(); + $includedFiles = GlobalState::getIncludedFilesAsString(); + $iniSettings = GlobalState::getIniSettingsAsString(); + } else { + $constants = ''; + + if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n"; + } else { + $globals = ''; + } + $includedFiles = ''; + $iniSettings = ''; + } + + $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false'; + $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false'; + $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false'; + $enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false'; + $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false'; + $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false'; + + if (\defined('PHPUNIT_COMPOSER_INSTALL')) { + $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true); + } else { + $composerAutoload = '\'\''; + } + + if (\defined('__PHPUNIT_PHAR__')) { + $phar = \var_export(__PHPUNIT_PHAR__, true); + } else { + $phar = '\'\''; + } + + if ($result->getCodeCoverage()) { + $codeCoverageFilter = $result->getCodeCoverage()->filter(); + } else { + $codeCoverageFilter = null; + } + + $data = \var_export(\serialize($this->data), true); + $dataName = \var_export($this->dataName, true); + $dependencyInput = \var_export(\serialize($this->dependencyInput), true); + $includePath = \var_export(\get_include_path(), true); + $codeCoverageFilter = \var_export(\serialize($codeCoverageFilter), true); + // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC + // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences + $data = "'." . $data . ".'"; + $dataName = "'.(" . $dataName . ").'"; + $dependencyInput = "'." . $dependencyInput . ".'"; + $includePath = "'." . $includePath . ".'"; + $codeCoverageFilter = "'." . $codeCoverageFilter . ".'"; + + $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; + + $var = [ + 'composerAutoload' => $composerAutoload, + 'phar' => $phar, + 'filename' => $class->getFileName(), + 'className' => $class->getName(), + 'collectCodeCoverageInformation' => $coverage, + 'data' => $data, + 'dataName' => $dataName, + 'dependencyInput' => $dependencyInput, + 'constants' => $constants, + 'globals' => $globals, + 'include_path' => $includePath, + 'included_files' => $includedFiles, + 'iniSettings' => $iniSettings, + 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything, + 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests, + 'enforcesTimeLimit' => $enforcesTimeLimit, + 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests, + 'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests, + 'codeCoverageFilter' => $codeCoverageFilter, + 'configurationFilePath' => $configurationFilePath, + 'name' => $this->getName(false), + ]; + + if (!$runEntireClass) { + $var['methodName'] = $this->name; + } + + $template->setVar( + $var + ); + + $php = AbstractPhpProcess::factory(); + $php->runTestJob($template->render(), $this, $result); + } else { + $result->run($this); + } + + if (isset($oldErrorHandlerSetting)) { + $result->convertErrorsToExceptions($oldErrorHandlerSetting); + } + + $this->result = null; + + return $result; + } + + /** + * @throws \Throwable + */ + public function runBare(): void + { + $this->numAssertions = 0; + + $this->snapshotGlobalState(); + $this->startOutputBuffering(); + \clearstatcache(); + $currentWorkingDirectory = \getcwd(); + + $hookMethods = \PHPUnit\Util\Test::getHookMethods(\get_class($this)); + + $hasMetRequirements = false; + + try { + $this->checkRequirements(); + $hasMetRequirements = true; + + if ($this->inIsolation) { + foreach ($hookMethods['beforeClass'] as $method) { + $this->$method(); + } + } + + $this->setExpectedExceptionFromAnnotation(); + $this->setDoesNotPerformAssertionsFromAnnotation(); + + foreach ($hookMethods['before'] as $method) { + $this->$method(); + } + + $this->assertPreConditions(); + $this->testResult = $this->runTest(); + $this->verifyMockObjects(); + $this->assertPostConditions(); + + if (!empty($this->warnings)) { + throw new Warning( + \implode( + "\n", + \array_unique($this->warnings) + ) + ); + } + + $this->status = BaseTestRunner::STATUS_PASSED; + } catch (IncompleteTest $e) { + $this->status = BaseTestRunner::STATUS_INCOMPLETE; + $this->statusMessage = $e->getMessage(); + } catch (SkippedTest $e) { + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->statusMessage = $e->getMessage(); + } catch (Warning $e) { + $this->status = BaseTestRunner::STATUS_WARNING; + $this->statusMessage = $e->getMessage(); + } catch (AssertionFailedError $e) { + $this->status = BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } catch (PredictionException $e) { + $this->status = BaseTestRunner::STATUS_FAILURE; + $this->statusMessage = $e->getMessage(); + } catch (Throwable $_e) { + $e = $_e; + $this->status = BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $_e->getMessage(); + } + + $this->mockObjects = []; + $this->prophet = null; + + // Tear down the fixture. An exception raised in tearDown() will be + // caught and passed on when no exception was raised before. + try { + if ($hasMetRequirements) { + foreach ($hookMethods['after'] as $method) { + $this->$method(); + } + + if ($this->inIsolation) { + foreach ($hookMethods['afterClass'] as $method) { + $this->$method(); + } + } + } + } catch (Throwable $_e) { + $e = $e ?? $_e; + } + + try { + $this->stopOutputBuffering(); + } catch (RiskyTestError $_e) { + $e = $e ?? $_e; + } + + if (isset($_e)) { + $this->status = BaseTestRunner::STATUS_ERROR; + $this->statusMessage = $_e->getMessage(); + } + + \clearstatcache(); + + if ($currentWorkingDirectory != \getcwd()) { + \chdir($currentWorkingDirectory); + } + + $this->restoreGlobalState(); + $this->unregisterCustomComparators(); + $this->cleanupIniSettings(); + $this->cleanupLocaleSettings(); + + // Perform assertion on output. + if (!isset($e)) { + try { + if ($this->outputExpectedRegex !== null) { + $this->assertRegExp($this->outputExpectedRegex, $this->output); + } elseif ($this->outputExpectedString !== null) { + $this->assertEquals($this->outputExpectedString, $this->output); + } + } catch (Throwable $_e) { + $e = $_e; + } + } + + // Workaround for missing "finally". + if (isset($e)) { + if ($e instanceof PredictionException) { + $e = new AssertionFailedError($e->getMessage()); + } + + $this->onNotSuccessfulTest($e); + } + } + + public function setName(string $name): void + { + $this->name = $name; + } + + /** + * @param string[] $dependencies + */ + public function setDependencies(array $dependencies): void + { + $this->dependencies = $dependencies; + } + + public function getDependencies(): array + { + return $this->dependencies; + } + + public function hasDependencies(): bool + { + return \count($this->dependencies) > 0; + } + + public function setDependencyInput(array $dependencyInput): void + { + $this->dependencyInput = $dependencyInput; + } + + public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState): void + { + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + } + + public function setBackupGlobals(?bool $backupGlobals): void + { + if ($this->backupGlobals === null && $backupGlobals !== null) { + $this->backupGlobals = $backupGlobals; + } + } + + public function setBackupStaticAttributes(?bool $backupStaticAttributes): void + { + if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) { + $this->backupStaticAttributes = $backupStaticAttributes; + } + } + + public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void + { + if ($this->runTestInSeparateProcess === null) { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } + } + + public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void + { + if ($this->runClassInSeparateProcess === null) { + $this->runClassInSeparateProcess = $runClassInSeparateProcess; + } + } + + public function setPreserveGlobalState(bool $preserveGlobalState): void + { + $this->preserveGlobalState = $preserveGlobalState; + } + + public function setInIsolation(bool $inIsolation): void + { + $this->inIsolation = $inIsolation; + } + + public function isInIsolation(): bool + { + return $this->inIsolation; + } + + public function getResult() + { + return $this->testResult; + } + + public function setResult($result): void + { + $this->testResult = $result; + } + + public function setOutputCallback(callable $callback): void + { + $this->outputCallback = $callback; + } + + public function getTestResultObject(): ?TestResult + { + return $this->result; + } + + public function setTestResultObject(TestResult $result): void + { + $this->result = $result; + } + + public function registerMockObject(MockObject $mockObject): void + { + $this->mockObjects[] = $mockObject; + } + + /** + * Returns a builder object to create mock objects using a fluent interface. + * + * @param string|string[] $className + */ + public function getMockBuilder($className): MockBuilder + { + return new MockBuilder($this, $className); + } + + public function addToAssertionCount(int $count): void + { + $this->numAssertions += $count; + } + + /** + * Returns the number of assertions performed by this test. + */ + public function getNumAssertions(): int + { + return $this->numAssertions; + } + + public function usesDataProvider(): bool + { + return !empty($this->data); + } + + public function dataDescription(): string + { + return \is_string($this->dataName) ? $this->dataName : ''; + } + + /** + * @return int|string + */ + public function dataName() + { + return $this->dataName; + } + + public function registerComparator(Comparator $comparator): void + { + ComparatorFactory::getInstance()->register($comparator); + + $this->customComparators[] = $comparator; + } + + public function getDataSetAsString(bool $includeData = true): string + { + $buffer = ''; + + if (!empty($this->data)) { + if (\is_int($this->dataName)) { + $buffer .= \sprintf(' with data set #%d', $this->dataName); + } else { + $buffer .= \sprintf(' with data set "%s"', $this->dataName); + } + + $exporter = new Exporter; + + if ($includeData) { + $buffer .= \sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); + } + } + + return $buffer; + } + + /** + * Gets the data set of a TestCase. + */ + public function getProvidedData(): array + { + return $this->data; + } + + public function addWarning(string $warning): void + { + $this->warnings[] = $warning; + } + + /** + * Override to run the test and assert its state. + * + * @throws AssertionFailedError + * @throws Exception + * @throws ExpectationFailedException + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @throws Throwable + */ + protected function runTest() + { + if ($this->name === null) { + throw new Exception( + 'PHPUnit\Framework\TestCase::$name must not be null.' + ); + } + + $testArguments = \array_merge($this->data, $this->dependencyInput); + + $this->registerMockObjectsFromTestArguments($testArguments); + + try { + $testResult = $this->{$this->name}(...\array_values($testArguments)); + } catch (Throwable $exception) { + if (!$this->checkExceptionExpectations($exception)) { + throw $exception; + } + + if ($this->expectedException !== null) { + $this->assertThat( + $exception, + new ExceptionConstraint( + $this->expectedException + ) + ); + } + + if ($this->expectedExceptionMessage !== null) { + $this->assertThat( + $exception, + new ExceptionMessage( + $this->expectedExceptionMessage + ) + ); + } + + if ($this->expectedExceptionMessageRegExp !== null) { + $this->assertThat( + $exception, + new ExceptionMessageRegularExpression( + $this->expectedExceptionMessageRegExp + ) + ); + } + + if ($this->expectedExceptionCode !== null) { + $this->assertThat( + $exception, + new ExceptionCode( + $this->expectedExceptionCode + ) + ); + } + + return; + } + + if ($this->expectedException !== null) { + $this->assertThat( + null, + new ExceptionConstraint( + $this->expectedException + ) + ); + } elseif ($this->expectedExceptionMessage !== null) { + $this->numAssertions++; + + throw new AssertionFailedError( + \sprintf( + 'Failed asserting that exception with message "%s" is thrown', + $this->expectedExceptionMessage + ) + ); + } elseif ($this->expectedExceptionMessageRegExp !== null) { + $this->numAssertions++; + + throw new AssertionFailedError( + \sprintf( + 'Failed asserting that exception with message matching "%s" is thrown', + $this->expectedExceptionMessageRegExp + ) + ); + } elseif ($this->expectedExceptionCode !== null) { + $this->numAssertions++; + + throw new AssertionFailedError( + \sprintf( + 'Failed asserting that exception with code "%s" is thrown', + $this->expectedExceptionCode + ) + ); + } + + return $testResult; + } + + /** + * This method is a wrapper for the ini_set() function that automatically + * resets the modified php.ini setting to its original value after the + * test is run. + * + * @throws Exception + */ + protected function iniSet(string $varName, $newValue): void + { + $currentValue = \ini_set($varName, $newValue); + + if ($currentValue !== false) { + $this->iniSettings[$varName] = $currentValue; + } else { + throw new Exception( + \sprintf( + 'INI setting "%s" could not be set to "%s".', + $varName, + $newValue + ) + ); + } + } + + /** + * This method is a wrapper for the setlocale() function that automatically + * resets the locale to its original value after the test is run. + * + * @throws Exception + */ + protected function setLocale(...$args): void + { + if (\count($args) < 2) { + throw new Exception; + } + + [$category, $locale] = $args; + + if (\defined('LC_MESSAGES')) { + $categories[] = \LC_MESSAGES; + } + + if (!\in_array($category, self::LOCALE_CATEGORIES, true)) { + throw new Exception; + } + + if (!\is_array($locale) && !\is_string($locale)) { + throw new Exception; + } + + $this->locale[$category] = \setlocale($category, 0); + + $result = \setlocale(...$args); + + if ($result === false) { + throw new Exception( + 'The locale functionality is not implemented on your platform, ' . + 'the specified locale does not exist or the category name is ' . + 'invalid.' + ); + } + } + + /** + * Returns a test double for the specified class. + * + * @param string|string[] $originalClassName + * + * @throws Exception + * @throws \InvalidArgumentException + */ + protected function createMock($originalClassName): MockObject + { + return $this->getMockBuilder($originalClassName) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->getMock(); + } + + /** + * Returns a configured test double for the specified class. + * + * @param string|string[] $originalClassName + * + * @throws Exception + * @throws \InvalidArgumentException + */ + protected function createConfiguredMock($originalClassName, array $configuration): MockObject + { + $o = $this->createMock($originalClassName); + + foreach ($configuration as $method => $return) { + $o->method($method)->willReturn($return); + } + + return $o; + } + + /** + * Returns a partial test double for the specified class. + * + * @param string|string[] $originalClassName + * @param string[] $methods + * + * @throws Exception + * @throws \InvalidArgumentException + */ + protected function createPartialMock($originalClassName, array $methods): MockObject + { + return $this->getMockBuilder($originalClassName) + ->disableOriginalConstructor() + ->disableOriginalClone() + ->disableArgumentCloning() + ->disallowMockingUnknownTypes() + ->setMethods(empty($methods) ? null : $methods) + ->getMock(); + } + + /** + * Returns a test proxy for the specified class. + * + * @throws Exception + * @throws \InvalidArgumentException + */ + protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject + { + return $this->getMockBuilder($originalClassName) + ->setConstructorArgs($constructorArguments) + ->enableProxyingToOriginalMethods() + ->getMock(); + } + + /** + * Mocks the specified class and returns the name of the mocked class. + * + * @param string $originalClassName + * @param array $methods + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * + * @throws Exception + * @throws ReflectionException + * @throws \InvalidArgumentException + */ + protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false): string + { + $mock = $this->getMockObjectGenerator()->getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + + return \get_class($mock); + } + + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods are not mocked by default. + * To mock concrete methods, use the 7th parameter ($mockedMethods). + * + * @param string $originalClassName + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @throws Exception + * @throws ReflectionException + * @throws \InvalidArgumentException + */ + protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject + { + $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass( + $originalClassName, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $mockedMethods, + $cloneArguments + ); + + $this->registerMockObject($mockObject); + + return $mockObject; + } + + /** + * Returns a mock object based on the given WSDL file. + * + * @param string $wsdlFile + * @param string $originalClassName + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param array $options An array of options passed to SOAPClient::_construct + * + * @throws Exception + * @throws ReflectionException + * @throws \InvalidArgumentException + */ + protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []): MockObject + { + if ($originalClassName === '') { + $fileName = \pathinfo(\basename(\parse_url(http://github.com/$wsdlFile)['path']), \PATHINFO_FILENAME); + $originalClassName = \preg_replace('/[^a-zA-Z0-9_]/', '', $fileName); + } + + if (!\class_exists($originalClassName)) { + eval( + $this->getMockObjectGenerator()->generateClassFromWsdl( + $wsdlFile, + $originalClassName, + $methods, + $options + ) + ); + } + + $mockObject = $this->getMockObjectGenerator()->getMock( + $originalClassName, + $methods, + ['', $options], + $mockClassName, + $callOriginalConstructor, + false, + false + ); + + $this->registerMockObject($mockObject); + + return $mockObject; + } + + /** + * Returns a mock object for the specified trait with all abstract methods + * of the trait mocked. Concrete methods to mock can be specified with the + * `$mockedMethods` parameter. + * + * @param string $traitName + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @throws Exception + * @throws ReflectionException + * @throws \InvalidArgumentException + */ + protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject + { + $mockObject = $this->getMockObjectGenerator()->getMockForTrait( + $traitName, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $mockedMethods, + $cloneArguments + ); + + $this->registerMockObject($mockObject); + + return $mockObject; + } + + /** + * Returns an object for the specified trait. + * + * @param string $traitName + * @param string $traitClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * + * @throws Exception + * @throws ReflectionException + * @throws \InvalidArgumentException + * + * @return object + */ + protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)/*: object*/ + { + return $this->getMockObjectGenerator()->getObjectForTrait( + $traitName, + $arguments, + $traitClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload + ); + } + + /** + * @param null|string $classOrInterface + * + * @throws Prophecy\Exception\Doubler\ClassNotFoundException + * @throws Prophecy\Exception\Doubler\DoubleException + * @throws Prophecy\Exception\Doubler\InterfaceNotFoundException + */ + protected function prophesize($classOrInterface = null): ObjectProphecy + { + return $this->getProphet()->prophesize($classOrInterface); + } + + /** + * Creates a default TestResult object. + */ + protected function createResult(): TestResult + { + return new TestResult; + } + + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called between setUp() and test. + */ + protected function assertPreConditions()/* The :void return type declaration that should be here would cause a BC issue */ + { + } + + /** + * Performs assertions shared by all tests of a test case. + * + * This method is called between test and tearDown(). + */ + protected function assertPostConditions()/* The :void return type declaration that should be here would cause a BC issue */ + { + } + + /** + * This method is called when a test method did not execute successfully. + * + * @throws Throwable + */ + protected function onNotSuccessfulTest(Throwable $t)/* The :void return type declaration that should be here would cause a BC issue */ + { + throw $t; + } + + private function setExpectedExceptionFromAnnotation(): void + { + try { + $expectedException = \PHPUnit\Util\Test::getExpectedException( + \get_class($this), + $this->name + ); + + if ($expectedException !== false) { + $this->expectException($expectedException['class']); + + if ($expectedException['code'] !== null) { + $this->expectExceptionCode($expectedException['code']); + } + + if ($expectedException['message'] !== '') { + $this->expectExceptionMessage($expectedException['message']); + } elseif ($expectedException['message_regex'] !== '') { + $this->expectExceptionMessageRegExp($expectedException['message_regex']); + } + } + } catch (ReflectionException $e) { + } + } + + private function setUseErrorHandlerFromAnnotation(): void + { + try { + $useErrorHandler = \PHPUnit\Util\Test::getErrorHandlerSettings( + \get_class($this), + $this->name + ); + + if ($useErrorHandler !== null) { + $this->setUseErrorHandler($useErrorHandler); + } + } catch (ReflectionException $e) { + } + } + + private function checkRequirements(): void + { + if (!$this->name || !\method_exists($this, $this->name)) { + return; + } + + $missingRequirements = \PHPUnit\Util\Test::getMissingRequirements( + \get_class($this), + $this->name + ); + + if (!empty($missingRequirements)) { + $this->markTestSkipped(\implode(\PHP_EOL, $missingRequirements)); + } + } + + private function verifyMockObjects(): void + { + foreach ($this->mockObjects as $mockObject) { + if ($mockObject->__phpunit_hasMatchers()) { + $this->numAssertions++; + } + + $mockObject->__phpunit_verify( + $this->shouldInvocationMockerBeReset($mockObject) + ); + } + + if ($this->prophet !== null) { + try { + $this->prophet->checkPredictions(); + } catch (Throwable $t) { + /* Intentionally left empty */ + } + + foreach ($this->prophet->getProphecies() as $objectProphecy) { + foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { + /** @var MethodProphecy[] $methodProphecies */ + foreach ($methodProphecies as $methodProphecy) { + $this->numAssertions += \count($methodProphecy->getCheckedPredictions()); + } + } + } + + if (isset($t)) { + throw $t; + } + } + } + + private function handleDependencies(): bool + { + if (!empty($this->dependencies) && !$this->inIsolation) { + $className = \get_class($this); + $passed = $this->result->passed(); + $passedKeys = \array_keys($passed); + $numKeys = \count($passedKeys); + + for ($i = 0; $i < $numKeys; $i++) { + $pos = \strpos($passedKeys[$i], ' with data set'); + + if ($pos !== false) { + $passedKeys[$i] = \substr($passedKeys[$i], 0, $pos); + } + } + + $passedKeys = \array_flip(\array_unique($passedKeys)); + + foreach ($this->dependencies as $dependency) { + $deepClone = false; + $shallowClone = false; + + if (\strpos($dependency, 'clone ') === 0) { + $deepClone = true; + $dependency = \substr($dependency, \strlen('clone ')); + } elseif (\strpos($dependency, '!clone ') === 0) { + $deepClone = false; + $dependency = \substr($dependency, \strlen('!clone ')); + } + + if (\strpos($dependency, 'shallowClone ') === 0) { + $shallowClone = true; + $dependency = \substr($dependency, \strlen('shallowClone ')); + } elseif (\strpos($dependency, '!shallowClone ') === 0) { + $shallowClone = false; + $dependency = \substr($dependency, \strlen('!shallowClone ')); + } + + if (\strpos($dependency, '::') === false) { + $dependency = $className . '::' . $dependency; + } + + if (!isset($passedKeys[$dependency])) { + if (!\is_callable($dependency, false, $callableName) || $dependency !== $callableName) { + $this->markWarningForUncallableDependency($dependency); + } else { + $this->markSkippedForMissingDependecy($dependency); + } + + return false; + } + + if (isset($passed[$dependency])) { + if ($passed[$dependency]['size'] != \PHPUnit\Util\Test::UNKNOWN && + $this->getSize() != \PHPUnit\Util\Test::UNKNOWN && + $passed[$dependency]['size'] > $this->getSize()) { + $this->result->addError( + $this, + new SkippedTestError( + 'This test depends on a test that is larger than itself.' + ), + 0 + ); + + return false; + } + + if ($deepClone) { + $deepCopy = new DeepCopy; + $deepCopy->skipUncloneable(false); + + $this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']); + } elseif ($shallowClone) { + $this->dependencyInput[$dependency] = clone $passed[$dependency]['result']; + } else { + $this->dependencyInput[$dependency] = $passed[$dependency]['result']; + } + } else { + $this->dependencyInput[$dependency] = null; + } + } + } + + return true; + } + + private function markSkippedForMissingDependecy(string $dependency): void + { + $this->status = BaseTestRunner::STATUS_SKIPPED; + $this->result->startTest($this); + $this->result->addError( + $this, + new SkippedTestError( + \sprintf( + 'This test depends on "%s" to pass.', + $dependency + ) + ), + 0 + ); + $this->result->endTest($this, 0); + } + + private function markWarningForUncallableDependency(string $dependency): void + { + $this->status = BaseTestRunner::STATUS_WARNING; + $this->result->startTest($this); + $this->result->addWarning( + $this, + new Warning( + \sprintf( + 'This test depends on "%s" which does not exist.', + $dependency + ) + ), + 0 + ); + $this->result->endTest($this, 0); + } + + /** + * Get the mock object generator, creating it if it doesn't exist. + */ + private function getMockObjectGenerator(): MockGenerator + { + if ($this->mockObjectGenerator === null) { + $this->mockObjectGenerator = new MockGenerator; + } + + return $this->mockObjectGenerator; + } + + private function startOutputBuffering(): void + { + \ob_start(); + + $this->outputBufferingActive = true; + $this->outputBufferingLevel = \ob_get_level(); + } + + /** + * @throws RiskyTestError + */ + private function stopOutputBuffering(): void + { + if (\ob_get_level() !== $this->outputBufferingLevel) { + while (\ob_get_level() >= $this->outputBufferingLevel) { + \ob_end_clean(); + } + + throw new RiskyTestError( + 'Test code or tested code did not (only) close its own output buffers' + ); + } + + $this->output = \ob_get_contents(); + + if ($this->outputCallback !== false) { + $this->output = (string) \call_user_func($this->outputCallback, $this->output); + } + + \ob_end_clean(); + + $this->outputBufferingActive = false; + $this->outputBufferingLevel = \ob_get_level(); + } + + private function snapshotGlobalState(): void + { + if ($this->runTestInSeparateProcess || $this->inIsolation || + (!$this->backupGlobals === true && !$this->backupStaticAttributes)) { + return; + } + + $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === true); + } + + /** + * @throws RiskyTestError + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \InvalidArgumentException + */ + private function restoreGlobalState(): void + { + if (!$this->snapshot instanceof Snapshot) { + return; + } + + if ($this->beStrictAboutChangesToGlobalState) { + try { + $this->compareGlobalStateSnapshots( + $this->snapshot, + $this->createGlobalStateSnapshot($this->backupGlobals === true) + ); + } catch (RiskyTestError $rte) { + // Intentionally left empty + } + } + + $restorer = new Restorer; + + if ($this->backupGlobals === true) { + $restorer->restoreGlobalVariables($this->snapshot); + } + + if ($this->backupStaticAttributes) { + $restorer->restoreStaticAttributes($this->snapshot); + } + + $this->snapshot = null; + + if (isset($rte)) { + throw $rte; + } + } + + private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot + { + $blacklist = new Blacklist; + + foreach ($this->backupGlobalsBlacklist as $globalVariable) { + $blacklist->addGlobalVariable($globalVariable); + } + + if (!\defined('PHPUNIT_TESTSUITE')) { + $blacklist->addClassNamePrefix('PHPUnit'); + $blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage'); + $blacklist->addClassNamePrefix('SebastianBergmann\FileIterator'); + $blacklist->addClassNamePrefix('SebastianBergmann\Invoker'); + $blacklist->addClassNamePrefix('SebastianBergmann\Timer'); + $blacklist->addClassNamePrefix('PHP_Token'); + $blacklist->addClassNamePrefix('Symfony'); + $blacklist->addClassNamePrefix('Text_Template'); + $blacklist->addClassNamePrefix('Doctrine\Instantiator'); + $blacklist->addClassNamePrefix('Prophecy'); + + foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { + foreach ($attributes as $attribute) { + $blacklist->addStaticAttribute($class, $attribute); + } + } + } + + return new Snapshot( + $blacklist, + $backupGlobals, + (bool) $this->backupStaticAttributes, + false, + false, + false, + false, + false, + false, + false + ); + } + + /** + * @throws RiskyTestError + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws \InvalidArgumentException + */ + private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void + { + $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; + + if ($backupGlobals) { + $this->compareGlobalStateSnapshotPart( + $before->globalVariables(), + $after->globalVariables(), + "--- Global variables before the test\n+++ Global variables after the test\n" + ); + + $this->compareGlobalStateSnapshotPart( + $before->superGlobalVariables(), + $after->superGlobalVariables(), + "--- Super-global variables before the test\n+++ Super-global variables after the test\n" + ); + } + + if ($this->backupStaticAttributes) { + $this->compareGlobalStateSnapshotPart( + $before->staticAttributes(), + $after->staticAttributes(), + "--- Static attributes before the test\n+++ Static attributes after the test\n" + ); + } + } + + /** + * @throws RiskyTestError + */ + private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void + { + if ($before != $after) { + $differ = new Differ($header); + $exporter = new Exporter; + + $diff = $differ->diff( + $exporter->export($before), + $exporter->export($after) + ); + + throw new RiskyTestError( + $diff + ); + } + } + + private function getProphet(): Prophet + { + if ($this->prophet === null) { + $this->prophet = new Prophet; + } + + return $this->prophet; + } + + /** + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + */ + private function shouldInvocationMockerBeReset(MockObject $mock): bool + { + $enumerator = new Enumerator; + + foreach ($enumerator->enumerate($this->dependencyInput) as $object) { + if ($mock === $object) { + return false; + } + } + + if (!\is_array($this->testResult) && !\is_object($this->testResult)) { + return true; + } + + return !\in_array($mock, $enumerator->enumerate($this->testResult), true); + } + + /** + * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException + * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void + { + if ($this->registerMockObjectsFromTestArgumentsRecursively) { + $enumerator = new Enumerator; + + foreach ($enumerator->enumerate($testArguments) as $object) { + if ($object instanceof MockObject) { + $this->registerMockObject($object); + } + } + } else { + foreach ($testArguments as $testArgument) { + if ($testArgument instanceof MockObject) { + if ($this->isCloneable($testArgument)) { + $testArgument = clone $testArgument; + } + + $this->registerMockObject($testArgument); + } elseif (\is_array($testArgument) && !\in_array($testArgument, $visited, true)) { + $visited[] = $testArgument; + + $this->registerMockObjectsFromTestArguments( + $testArgument, + $visited + ); + } + } + } + } + + private function setDoesNotPerformAssertionsFromAnnotation(): void + { + $annotations = $this->getAnnotations(); + + if (isset($annotations['method']['doesNotPerformAssertions'])) { + $this->doesNotPerformAssertions = true; + } + } + + private function isCloneable(MockObject $testArgument): bool + { + $reflector = new ReflectionObject($testArgument); + + if (!$reflector->isCloneable()) { + return false; + } + + if ($reflector->hasMethod('__clone') && + $reflector->getMethod('__clone')->isPublic()) { + return true; + } + + return false; + } + + private function unregisterCustomComparators(): void + { + $factory = ComparatorFactory::getInstance(); + + foreach ($this->customComparators as $comparator) { + $factory->unregister($comparator); + } + + $this->customComparators = []; + } + + private function cleanupIniSettings(): void + { + foreach ($this->iniSettings as $varName => $oldValue) { + \ini_set($varName, $oldValue); + } + + $this->iniSettings = []; + } + + private function cleanupLocaleSettings(): void + { + foreach ($this->locale as $category => $locale) { + \setlocale($category, $locale); + } + + $this->locale = []; + } + + /** + * @throws ReflectionException + */ + private function checkExceptionExpectations(Throwable $throwable): bool + { + $result = false; + + if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) { + $result = true; + } + + if ($throwable instanceof Exception) { + $result = false; + } + + if (\is_string($this->expectedException)) { + $reflector = new ReflectionClass($this->expectedException); + + if ($this->expectedException === 'PHPUnit\Framework\Exception' || + $this->expectedException === '\PHPUnit\Framework\Exception' || + $reflector->isSubclassOf(Exception::class)) { + $result = true; + } + } + + return $result; + } + + private function runInSeparateProcess(): bool + { + return ($this->runTestInSeparateProcess === true || $this->runClassInSeparateProcess === true) && + $this->inIsolation !== true && !$this instanceof PhptTestCase; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +class BadMethodCallException extends \BadMethodCallException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * Interface for exceptions used by PHPUnit_MockObject. + */ +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit_Framework_MockObject_MockObject; + +interface MockObject extends PHPUnit_Framework_MockObject_MockObject +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use Exception; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker; +use PHPUnit\Framework\MockObject\Builder\Match; +use PHPUnit\Framework\MockObject\Builder\NamespaceMatch; +use PHPUnit\Framework\MockObject\Matcher\DeferredError; +use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation; +use PHPUnit\Framework\MockObject\Stub\MatcherCollection; + +/** + * Mocker for invocations which are sent from + * MockObject objects. + * + * Keeps track of all expectations and stubs as well as registering + * identifications for builders. + */ +class InvocationMocker implements MatcherCollection, Invokable, NamespaceMatch +{ + /** + * @var MatcherInvocation[] + */ + private $matchers = []; + + /** + * @var Match[] + */ + private $builderMap = []; + + /** + * @var string[] + */ + private $configurableMethods; + + /** + * @var bool + */ + private $returnValueGeneration; + + public function __construct(array $configurableMethods, bool $returnValueGeneration) + { + $this->configurableMethods = $configurableMethods; + $this->returnValueGeneration = $returnValueGeneration; + } + + public function addMatcher(MatcherInvocation $matcher): void + { + $this->matchers[] = $matcher; + } + + public function hasMatchers() + { + foreach ($this->matchers as $matcher) { + if ($matcher->hasMatchers()) { + return true; + } + } + + return false; + } + + /** + * @return null|bool + */ + public function lookupId($id) + { + if (isset($this->builderMap[$id])) { + return $this->builderMap[$id]; + } + } + + /** + * @throws RuntimeException + */ + public function registerId($id, Match $builder): void + { + if (isset($this->builderMap[$id])) { + throw new RuntimeException( + 'Match builder with id <' . $id . '> is already registered.' + ); + } + + $this->builderMap[$id] = $builder; + } + + /** + * @return BuilderInvocationMocker + */ + public function expects(MatcherInvocation $matcher) + { + return new BuilderInvocationMocker( + $this, + $matcher, + $this->configurableMethods + ); + } + + /** + * @throws Exception + */ + public function invoke(Invocation $invocation) + { + $exception = null; + $hasReturnValue = false; + $returnValue = null; + + foreach ($this->matchers as $match) { + try { + if ($match->matches($invocation)) { + $value = $match->invoked($invocation); + + if (!$hasReturnValue) { + $returnValue = $value; + $hasReturnValue = true; + } + } + } catch (Exception $e) { + $exception = $e; + } + } + + if ($exception !== null) { + throw $exception; + } + + if ($hasReturnValue) { + return $returnValue; + } + + if ($this->returnValueGeneration === false) { + $exception = new ExpectationFailedException( + \sprintf( + 'Return value inference disabled and no expectation set up for %s::%s()', + $invocation->getClassName(), + $invocation->getMethodName() + ) + ); + + if (\strtolower($invocation->getMethodName()) === '__tostring') { + $this->addMatcher(new DeferredError($exception)); + + return ''; + } + + throw $exception; + } + + return $invocation->generateReturnValue(); + } + + /** + * @return bool + */ + public function matches(Invocation $invocation) + { + foreach ($this->matchers as $matcher) { + if (!$matcher->matches($invocation)) { + return false; + } + } + + return true; + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * + * @return bool + */ + public function verify() + { + foreach ($this->matchers as $matcher) { + $matcher->verify(); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\SelfDescribing; + +/** + * An object that stubs the process of a normal method for a mock object. + * + * The stub object will replace the code for the stubbed method and return a + * specific value instead of the original value. + */ +interface Stub extends SelfDescribing +{ + /** + * Fakes the processing of the invocation $invocation by returning a + * specific value. + * + * @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers + */ + public function invoke(Invocation $invocation); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Interface for classes which must verify a given expectation. + */ +interface Verifiable +{ + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify(); +} + + public function {method_name}({arguments}) + { + } + + @trigger_error({deprecation}, E_USER_DEPRECATED); + + public function method() + { + $any = new \PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount; + $expects = $this->expects($any); + + return call_user_func_array([$expects, 'method'], func_get_args()); + } +{namespace}class {class_name} extends \SoapClient +{ + public function __construct($wsdl, array $options) + { + parent::__construct('{wsdl}', $options); + } +{methods}} +{prologue}class {class_name} +{ + use {trait_name}; +} + + {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} + { + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $this->__phpunit_getInvocationMocker()->invoke( + new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + + return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); + } + + {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} + { + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $this->__phpunit_getInvocationMocker()->invoke( + new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + + call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); + } + + {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} + {{deprecation} + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $this->__phpunit_getInvocationMocker()->invoke( + new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + } +{prologue}{class_declaration} +{ + private $__phpunit_invocationMocker; + private $__phpunit_originalObject; + private $__phpunit_configurable = {configurable}; + private $__phpunit_returnValueGeneration = true; + +{clone}{mocked_methods} + public function expects(\PHPUnit\Framework\MockObject\Matcher\Invocation $matcher) + { + return $this->__phpunit_getInvocationMocker()->expects($matcher); + } +{method} + public function __phpunit_setOriginalObject($originalObject) + { + $this->__phpunit_originalObject = $originalObject; + } + + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) + { + $this->__phpunit_returnValueGeneration = $returnValueGeneration; + } + + public function __phpunit_getInvocationMocker() + { + if ($this->__phpunit_invocationMocker === null) { + $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationMocker($this->__phpunit_configurable, $this->__phpunit_returnValueGeneration); + } + + return $this->__phpunit_invocationMocker; + } + + public function __phpunit_hasMatchers() + { + return $this->__phpunit_getInvocationMocker()->hasMatchers(); + } + + public function __phpunit_verify(bool $unsetInvocationMocker = true) + { + $this->__phpunit_getInvocationMocker()->verify(); + + if ($unsetInvocationMocker) { + $this->__phpunit_invocationMocker = null; + } + } +}{epilogue} + + {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} + {{deprecation} + $__phpunit_arguments = [{arguments_call}]; + $__phpunit_count = func_num_args(); + + if ($__phpunit_count > {arguments_count}) { + $__phpunit_arguments_tmp = func_get_args(); + + for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { + $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; + } + } + + $__phpunit_result = $this->__phpunit_getInvocationMocker()->invoke( + new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( + '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} + ) + ); + + return $__phpunit_result; + } + + {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} + { + throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object'); + } + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + } + public function __clone() + { + $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); + parent::__clone(); + } + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * Interface for invocations. + */ +interface Invocation +{ + /** + * @return mixed mocked return value + */ + public function generateReturnValue(); + + public function getClassName(): string; + + public function getMethodName(): string; + + public function getParameters(): array; + + public function getReturnType(): string; + + public function isReturnTypeNullable(): bool; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Invocation; + +use PHPUnit\Framework\MockObject\Generator; +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\SelfDescribing; +use ReflectionObject; +use SebastianBergmann\Exporter\Exporter; + +/** + * Represents a static invocation. + */ +class StaticInvocation implements Invocation, SelfDescribing +{ + /** + * @var array + */ + private static $uncloneableExtensions = [ + 'mysqli' => true, + 'SQLite' => true, + 'sqlite3' => true, + 'tidy' => true, + 'xmlwriter' => true, + 'xsl' => true, + ]; + + /** + * @var array + */ + private static $uncloneableClasses = [ + 'Closure', + 'COMPersistHelper', + 'IteratorIterator', + 'RecursiveIteratorIterator', + 'SplFileObject', + 'PDORow', + 'ZipArchive', + ]; + + /** + * @var string + */ + private $className; + + /** + * @var string + */ + private $methodName; + + /** + * @var array + */ + private $parameters; + + /** + * @var string + */ + private $returnType; + + /** + * @var bool + */ + private $isReturnTypeNullable = false; + + /** + * @param string $className + * @param string $methodName + * @param string $returnType + * @param bool $cloneObjects + */ + public function __construct($className, $methodName, array $parameters, $returnType, $cloneObjects = false) + { + $this->className = $className; + $this->methodName = $methodName; + $this->parameters = $parameters; + + if (\strtolower($methodName) === '__tostring') { + $returnType = 'string'; + } + + if (\strpos($returnType, '?') === 0) { + $returnType = \substr($returnType, 1); + $this->isReturnTypeNullable = true; + } + + $this->returnType = $returnType; + + if (!$cloneObjects) { + return; + } + + foreach ($this->parameters as $key => $value) { + if (\is_object($value)) { + $this->parameters[$key] = $this->cloneObject($value); + } + } + } + + public function getClassName(): string + { + return $this->className; + } + + public function getMethodName(): string + { + return $this->methodName; + } + + public function getParameters(): array + { + return $this->parameters; + } + + public function getReturnType(): string + { + return $this->returnType; + } + + public function isReturnTypeNullable(): bool + { + return $this->isReturnTypeNullable; + } + + /** + * @throws \ReflectionException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + * @throws \PHPUnit\Framework\Exception + * + * @return mixed Mocked return value + */ + public function generateReturnValue() + { + if ($this->isReturnTypeNullable) { + return; + } + + switch (\strtolower($this->returnType)) { + case '': + case 'void': + return; + + case 'string': + return ''; + + case 'float': + return 0.0; + + case 'int': + return 0; + + case 'bool': + return false; + + case 'array': + return []; + + case 'object': + return new \stdClass; + + case 'callable': + case 'closure': + return function (): void { + }; + + case 'traversable': + case 'generator': + case 'iterable': + $generator = function () { + yield; + }; + + return $generator(); + + default: + $generator = new Generator; + + return $generator->getMock($this->returnType, [], [], '', false); + } + } + + public function toString(): string + { + $exporter = new Exporter; + + return \sprintf( + '%s::%s(%s)%s', + $this->className, + $this->methodName, + \implode( + ', ', + \array_map( + [$exporter, 'shortenedExport'], + $this->parameters + ) + ), + $this->returnType ? \sprintf(': %s', $this->returnType) : '' + ); + } + + /** + * @param object $original + * + * @return object + */ + private function cloneObject($original) + { + $cloneable = null; + $object = new ReflectionObject($original); + + // Check the blacklist before asking PHP reflection to work around + // https://bugs.php.net/bug.php?id=53967 + if ($object->isInternal() && + isset(self::$uncloneableExtensions[$object->getExtensionName()])) { + $cloneable = false; + } + + if ($cloneable === null) { + foreach (self::$uncloneableClasses as $class) { + if ($original instanceof $class) { + $cloneable = false; + + break; + } + } + } + + if ($cloneable === null) { + $cloneable = $object->isCloneable(); + } + + if ($cloneable === null && $object->hasMethod('__clone')) { + $method = $object->getMethod('__clone'); + $cloneable = $method->isPublic(); + } + + if ($cloneable === null) { + $cloneable = true; + } + + if ($cloneable) { + try { + return clone $original; + } catch (\Exception $e) { + return $original; + } + } else { + return $original; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Invocation; + +/** + * Represents a non-static invocation. + */ +class ObjectInvocation extends StaticInvocation +{ + /** + * @var object + */ + private $object; + + /** + * @param string $className + * @param string $methodName + * @param string $returnType + * @param object $object + * @param bool $cloneObjects + */ + public function __construct($className, $methodName, array $parameters, $returnType, $object, $cloneObjects = false) + { + parent::__construct($className, $methodName, $parameters, $returnType, $cloneObjects); + + $this->object = $object; + } + + public function getObject() + { + return $this->object; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use ReflectionClass; +use ReflectionException; +use ReflectionMethod; +use Text_Template; + +final class MockMethod +{ + /** + * @var Text_Template[] + */ + private static $templates = []; + + /** + * @var string + */ + private $className; + + /** + * @var string + */ + private $methodName; + + /** + * @var bool + */ + private $cloneArguments; + + /** + * @var string string + */ + private $modifier; + + /** + * @var string + */ + private $argumentsForDeclaration; + + /** + * @var string + */ + private $argumentsForCall; + + /** + * @var string + */ + private $returnType; + + /** + * @var string + */ + private $reference; + + /** + * @var bool + */ + private $callOriginalMethod; + + /** + * @var bool + */ + private $static; + + /** + * @var ?string + */ + private $deprecation; + + /** + * @var bool + */ + private $allowsReturnNull; + + public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self + { + if ($method->isPrivate()) { + $modifier = 'private'; + } elseif ($method->isProtected()) { + $modifier = 'protected'; + } else { + $modifier = 'public'; + } + + if ($method->isStatic()) { + $modifier .= ' static'; + } + + if ($method->returnsReference()) { + $reference = '&'; + } else { + $reference = ''; + } + + if ($method->hasReturnType()) { + $returnType = (string) $method->getReturnType(); + } else { + $returnType = ''; + } + + $docComment = $method->getDocComment(); + + if (\is_string($docComment) + && \preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation) + ) { + $deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1])); + } else { + $deprecation = null; + } + + return new self( + $method->getDeclaringClass()->getName(), + $method->getName(), + $cloneArguments, + $modifier, + self::getMethodParameters($method), + self::getMethodParameters($method, true), + $returnType, + $reference, + $callOriginalMethod, + $method->isStatic(), + $deprecation, + $method->hasReturnType() && $method->getReturnType()->allowsNull() + ); + } + + public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self + { + return new self( + $fullClassName, + $methodName, + $cloneArguments, + 'public', + '', + '', + '', + '', + false, + false, + null, + false + ); + } + + public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, string $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation, bool $allowsReturnNull) + { + $this->className = $className; + $this->methodName = $methodName; + $this->cloneArguments = $cloneArguments; + $this->modifier = $modifier; + $this->argumentsForDeclaration = $argumentsForDeclaration; + $this->argumentsForCall = $argumentsForCall; + $this->returnType = $returnType; + $this->reference = $reference; + $this->callOriginalMethod = $callOriginalMethod; + $this->static = $static; + $this->deprecation = $deprecation; + $this->allowsReturnNull = $allowsReturnNull; + } + + public function getName(): string + { + return $this->methodName; + } + + /** + * @throws \ReflectionException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + * @throws \InvalidArgumentException + */ + public function generateCode(): string + { + if ($this->static) { + $templateFile = 'mocked_static_method.tpl'; + } elseif ($this->returnType === 'void') { + $templateFile = \sprintf( + '%s_method_void.tpl', + $this->callOriginalMethod ? 'proxied' : 'mocked' + ); + } else { + $templateFile = \sprintf( + '%s_method.tpl', + $this->callOriginalMethod ? 'proxied' : 'mocked' + ); + } + + $returnType = $this->returnType; + // @see https://bugs.php.net/bug.php?id=70722 + if ($returnType === 'self') { + $returnType = $this->className; + } + + // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/406 + if ($returnType === 'parent') { + $reflector = new ReflectionClass($this->className); + + $parentClass = $reflector->getParentClass(); + + if ($parentClass === false) { + throw new RuntimeException( + \sprintf( + 'Cannot mock %s::%s because "parent" return type declaration is used but %s does not have a parent class', + $this->className, + $this->methodName, + $this->className + ) + ); + } + + $returnType = $parentClass->getName(); + } + + $deprecation = $this->deprecation; + + if (null !== $this->deprecation) { + $deprecation = "The $this->className::$this->methodName method is deprecated ($this->deprecation)."; + $deprecationTemplate = $this->getTemplate('deprecation.tpl'); + + $deprecationTemplate->setVar([ + 'deprecation' => \var_export($deprecation, true), + ]); + + $deprecation = $deprecationTemplate->render(); + } + + $template = $this->getTemplate($templateFile); + + $template->setVar( + [ + 'arguments_decl' => $this->argumentsForDeclaration, + 'arguments_call' => $this->argumentsForCall, + 'return_delim' => $returnType ? ': ' : '', + 'return_type' => $this->allowsReturnNull ? '?' . $returnType : $returnType, + 'arguments_count' => !empty($this->argumentsForCall) ? \substr_count($this->argumentsForCall, ',') + 1 : 0, + 'class_name' => $this->className, + 'method_name' => $this->methodName, + 'modifier' => $this->modifier, + 'reference' => $this->reference, + 'clone_arguments' => $this->cloneArguments ? 'true' : 'false', + 'deprecation' => $deprecation, + ] + ); + + return $template->render(); + } + + private function getTemplate(string $template): Text_Template + { + $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; + + if (!isset(self::$templates[$filename])) { + self::$templates[$filename] = new Text_Template($filename); + } + + return self::$templates[$filename]; + } + + /** + * Returns the parameters of a function or method. + * + * @throws RuntimeException + */ + private static function getMethodParameters(ReflectionMethod $method, bool $forCall = false): string + { + $parameters = []; + + foreach ($method->getParameters() as $i => $parameter) { + $name = '$' . $parameter->getName(); + + /* Note: PHP extensions may use empty names for reference arguments + * or "..." for methods taking a variable number of arguments. + */ + if ($name === '$' || $name === '$...') { + $name = '$arg' . $i; + } + + if ($parameter->isVariadic()) { + if ($forCall) { + continue; + } + + $name = '...' . $name; + } + + $nullable = ''; + $default = ''; + $reference = ''; + $typeDeclaration = ''; + + if (!$forCall) { + if ($parameter->hasType() && $parameter->allowsNull()) { + $nullable = '?'; + } + + if ($parameter->hasType() && (string) $parameter->getType() !== 'self') { + $typeDeclaration = $parameter->getType() . ' '; + } else { + try { + $class = $parameter->getClass(); + } catch (ReflectionException $e) { + throw new RuntimeException( + \sprintf( + 'Cannot mock %s::%s() because a class or ' . + 'interface used in the signature is not loaded', + $method->getDeclaringClass()->getName(), + $method->getName() + ), + 0, + $e + ); + } + + if ($class !== null) { + $typeDeclaration = $class->getName() . ' '; + } + } + + if (!$parameter->isVariadic()) { + if ($parameter->isDefaultValueAvailable()) { + $value = $parameter->getDefaultValueConstantName(); + + if ($value === null) { + $value = \var_export($parameter->getDefaultValue(), true); + } elseif (!\defined($value)) { + $rootValue = \preg_replace('/^.*\\\\/', '', $value); + $value = \defined($rootValue) ? $rootValue : $value; + } + + $default = ' = ' . $value; + } elseif ($parameter->isOptional()) { + $default = ' = null'; + } + } + } + + if ($parameter->isPassedByReference()) { + $reference = '&'; + } + + $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default; + } + + return \implode(', ', $parameters); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +/** + * Interface for classes which can be invoked. + * + * The invocation will be taken from a mock object and passed to an object + * of this class. + */ +interface Invokable extends Verifiable +{ + /** + * Invokes the invocation object $invocation so that it can be checked for + * expectations or matched against stubs. + * + * @param Invocation $invocation The invocation object passed from mock object + * + * @return object + */ + public function invoke(Invocation $invocation); + + /** + * Checks if the invocation matches. + * + * @param Invocation $invocation The invocation object passed from mock object + * + * @return bool + */ + public function matches(Invocation $invocation); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Invocation matcher which allows any parameters to a method. + */ +class AnyParameters extends StatelessInvocation +{ + public function toString(): string + { + return 'with any parameters'; + } + + /** + * @return bool + */ + public function matches(BaseInvocation $invocation) + { + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Invocation matcher which checks if a method has been invoked at least one + * time. + * + * If the number of invocations is 0 it will throw an exception in verify. + */ +class InvokedAtLeastOnce extends InvokedRecorder +{ + public function toString(): string + { + return 'invoked at least once'; + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify(): void + { + $count = $this->getInvocationCount(); + + if ($count < 1) { + throw new ExpectationFailedException( + 'Expected invocation at least once but it never occurred.' + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsAnything; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Invocation matcher which looks for specific parameters in the invocations. + * + * Checks the parameters of all incoming invocations, the parameter list is + * checked against the defined constraints in $parameters. If the constraint + * is met it will return true in matches(). + */ +class Parameters extends StatelessInvocation +{ + /** + * @var Constraint[] + */ + private $parameters = []; + + /** + * @var BaseInvocation + */ + private $invocation; + + /** + * @var ExpectationFailedException + */ + private $parameterVerificationResult; + + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(array $parameters) + { + foreach ($parameters as $parameter) { + if (!($parameter instanceof Constraint)) { + $parameter = new IsEqual( + $parameter + ); + } + + $this->parameters[] = $parameter; + } + } + + public function toString(): string + { + $text = 'with parameter'; + + foreach ($this->parameters as $index => $parameter) { + if ($index > 0) { + $text .= ' and'; + } + + $text .= ' ' . $index . ' ' . $parameter->toString(); + } + + return $text; + } + + /** + * @throws \Exception + * + * @return bool + */ + public function matches(BaseInvocation $invocation) + { + $this->invocation = $invocation; + $this->parameterVerificationResult = null; + + try { + $this->parameterVerificationResult = $this->verify(); + + return $this->parameterVerificationResult; + } catch (ExpectationFailedException $e) { + $this->parameterVerificationResult = $e; + + throw $this->parameterVerificationResult; + } + } + + /** + * Checks if the invocation $invocation matches the current rules. If it + * does the matcher will get the invoked() method called which should check + * if an expectation is met. + * + * @throws ExpectationFailedException + * + * @return bool + */ + public function verify() + { + if (isset($this->parameterVerificationResult)) { + return $this->guardAgainstDuplicateEvaluationOfParameterConstraints(); + } + + if ($this->invocation === null) { + throw new ExpectationFailedException('Mocked method does not exist.'); + } + + if (\count($this->invocation->getParameters()) < \count($this->parameters)) { + $message = 'Parameter count for invocation %s is too low.'; + + // The user called `->with($this->anything())`, but may have meant + // `->withAnyParameters()`. + // + // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199 + if (\count($this->parameters) === 1 && + \get_class($this->parameters[0]) === IsAnything::class) { + $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead."; + } + + throw new ExpectationFailedException( + \sprintf($message, $this->invocation->toString()) + ); + } + + foreach ($this->parameters as $i => $parameter) { + $parameter->evaluate( + $this->invocation->getParameters()[$i], + \sprintf( + 'Parameter %s for invocation %s does not match expected ' . + 'value.', + $i, + $this->invocation->toString() + ) + ); + } + + return true; + } + + /** + * @throws ExpectationFailedException + * + * @return bool + */ + private function guardAgainstDuplicateEvaluationOfParameterConstraints() + { + if ($this->parameterVerificationResult instanceof \Exception) { + throw $this->parameterVerificationResult; + } + + return (bool) $this->parameterVerificationResult; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Invocation matcher which does not care about previous state from earlier + * invocations. + * + * This abstract class can be implemented by matchers which does not care about + * state but only the current run-time value of the invocation itself. + */ +abstract class StatelessInvocation implements Invocation +{ + /** + * Registers the invocation $invocation in the object as being invoked. + * This will only occur after matches() returns true which means the + * current invocation is the correct one. + * + * The matcher can store information from the invocation which can later + * be checked in verify(), or it can check the values directly and throw + * and exception if an expectation is not met. + * + * If the matcher is a stub it will also have a return value. + * + * @param BaseInvocation $invocation Object containing information on a mocked or stubbed method which was invoked + */ + public function invoked(BaseInvocation $invocation) + { + } + + /** + * Checks if the invocation $invocation matches the current rules. If it does + * the matcher will get the invoked() method called which should check if an + * expectation is met. + * + * @return bool + */ + public function verify() + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Util\InvalidArgumentHelper; + +/** + * Invocation matcher which looks for a specific method name in the invocations. + * + * Checks the method name all incoming invocations, the name is checked against + * the defined constraint $constraint. If the constraint is met it will return + * true in matches(). + */ +class MethodName extends StatelessInvocation +{ + /** + * @var Constraint + */ + private $constraint; + + /** + * @param Constraint|string + * + * @throws Constraint + * @throws \PHPUnit\Framework\Exception + */ + public function __construct($constraint) + { + if (!$constraint instanceof Constraint) { + if (!\is_string($constraint)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + $constraint = new IsEqual( + $constraint, + 0, + 10, + false, + true + ); + } + + $this->constraint = $constraint; + } + + public function toString(): string + { + return 'method name ' . $this->constraint->toString(); + } + + /** + * @return bool + */ + public function matches(BaseInvocation $invocation) + { + return $this->constraint->evaluate($invocation->getMethodName(), '', true); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Invocation matcher which checks if a method was invoked at a certain index. + * + * If the expected index number does not match the current invocation index it + * will not match which means it skips all method and parameter matching. Only + * once the index is reached will the method and parameter start matching and + * verifying. + * + * If the index is never reached it will throw an exception in index. + */ +class InvokedAtIndex implements Invocation +{ + /** + * @var int + */ + private $sequenceIndex; + + /** + * @var int + */ + private $currentIndex = -1; + + /** + * @param int $sequenceIndex + */ + public function __construct($sequenceIndex) + { + $this->sequenceIndex = $sequenceIndex; + } + + public function toString(): string + { + return 'invoked at sequence index ' . $this->sequenceIndex; + } + + /** + * @return bool + */ + public function matches(BaseInvocation $invocation) + { + $this->currentIndex++; + + return $this->currentIndex == $this->sequenceIndex; + } + + public function invoked(BaseInvocation $invocation): void + { + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify(): void + { + if ($this->currentIndex < $this->sequenceIndex) { + throw new ExpectationFailedException( + \sprintf( + 'The expected invocation at index %s was never reached.', + $this->sequenceIndex + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Invocation matcher which checks if a method has been invoked a certain amount + * of times. + * If the number of invocations exceeds the value it will immediately throw an + * exception, + * If the number is less it will later be checked in verify() and also throw an + * exception. + */ +class InvokedCount extends InvokedRecorder +{ + /** + * @var int + */ + private $expectedCount; + + /** + * @param int $expectedCount + */ + public function __construct($expectedCount) + { + $this->expectedCount = $expectedCount; + } + + /** + * @return bool + */ + public function isNever() + { + return $this->expectedCount === 0; + } + + public function toString(): string + { + return 'invoked ' . $this->expectedCount . ' time(s)'; + } + + /** + * @throws ExpectationFailedException + */ + public function invoked(BaseInvocation $invocation): void + { + parent::invoked($invocation); + + $count = $this->getInvocationCount(); + + if ($count > $this->expectedCount) { + $message = $invocation->toString() . ' '; + + switch ($this->expectedCount) { + case 0: + $message .= 'was not expected to be called.'; + + break; + + case 1: + $message .= 'was not expected to be called more than once.'; + + break; + + default: + $message .= \sprintf( + 'was not expected to be called more than %d times.', + $this->expectedCount + ); + } + + throw new ExpectationFailedException($message); + } + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify(): void + { + $count = $this->getInvocationCount(); + + if ($count !== $this->expectedCount) { + throw new ExpectationFailedException( + \sprintf( + 'Method was expected to be called %d times, ' . + 'actually called %d times.', + $this->expectedCount, + $count + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Records invocations and provides convenience methods for checking them later + * on. + * This abstract class can be implemented by matchers which needs to check the + * number of times an invocation has occurred. + */ +abstract class InvokedRecorder implements Invocation +{ + /** + * @var BaseInvocation[] + */ + private $invocations = []; + + /** + * @return int + */ + public function getInvocationCount() + { + return \count($this->invocations); + } + + /** + * @return BaseInvocation[] + */ + public function getInvocations() + { + return $this->invocations; + } + + /** + * @return bool + */ + public function hasBeenInvoked() + { + return \count($this->invocations) > 0; + } + + public function invoked(BaseInvocation $invocation): void + { + $this->invocations[] = $invocation; + } + + /** + * @return bool + */ + public function matches(BaseInvocation $invocation) + { + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +/** + * Invocation matcher which looks for sets of specific parameters in the invocations. + * + * Checks the parameters of the incoming invocations, the parameter list is + * checked against the defined constraints in $parameters. If the constraint + * is met it will return true in matches(). + * + * It takes a list of match groups and and increases a call index after each invocation. + * So the first invocation uses the first group of constraints, the second the next and so on. + */ +class ConsecutiveParameters extends StatelessInvocation +{ + /** + * @var array + */ + private $parameterGroups = []; + + /** + * @var array + */ + private $invocations = []; + + /** + * @throws \PHPUnit\Framework\Exception + */ + public function __construct(array $parameterGroups) + { + foreach ($parameterGroups as $index => $parameters) { + foreach ($parameters as $parameter) { + if (!$parameter instanceof Constraint) { + $parameter = new IsEqual($parameter); + } + + $this->parameterGroups[$index][] = $parameter; + } + } + } + + public function toString(): string + { + return 'with consecutive parameters'; + } + + /** + * @throws \PHPUnit\Framework\ExpectationFailedException + * + * @return bool + */ + public function matches(BaseInvocation $invocation) + { + $this->invocations[] = $invocation; + $callIndex = \count($this->invocations) - 1; + + $this->verifyInvocation($invocation, $callIndex); + + return false; + } + + public function verify(): void + { + foreach ($this->invocations as $callIndex => $invocation) { + $this->verifyInvocation($invocation, $callIndex); + } + } + + /** + * Verify a single invocation + * + * @param int $callIndex + * + * @throws ExpectationFailedException + */ + private function verifyInvocation(BaseInvocation $invocation, $callIndex): void + { + if (!isset($this->parameterGroups[$callIndex])) { + // no parameter assertion for this call index + return; + } + + if ($invocation === null) { + throw new ExpectationFailedException( + 'Mocked method does not exist.' + ); + } + + $parameters = $this->parameterGroups[$callIndex]; + + if (\count($invocation->getParameters()) < \count($parameters)) { + throw new ExpectationFailedException( + \sprintf( + 'Parameter count for invocation %s is too low.', + $invocation->toString() + ) + ); + } + + foreach ($parameters as $i => $parameter) { + $parameter->evaluate( + $invocation->getParameters()[$i], + \sprintf( + 'Parameter %s for invocation #%d %s does not match expected ' . + 'value.', + $i, + $callIndex, + $invocation->toString() + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +/** + * Invocation matcher which checks if a method has been invoked zero or more + * times. This matcher will always match. + */ +class AnyInvokedCount extends InvokedRecorder +{ + public function toString(): string + { + return 'invoked zero or more times'; + } + + public function verify(): void + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; +use PHPUnit\Framework\MockObject\Verifiable; +use PHPUnit\Framework\SelfDescribing; + +/** + * Interface for classes which matches an invocation based on its + * method name, argument, order or call count. + */ +interface Invocation extends SelfDescribing, Verifiable +{ + /** + * Registers the invocation $invocation in the object as being invoked. + * This will only occur after matches() returns true which means the + * current invocation is the correct one. + * + * The matcher can store information from the invocation which can later + * be checked in verify(), or it can check the values directly and throw + * and exception if an expectation is not met. + * + * If the matcher is a stub it will also have a return value. + * + * @param BaseInvocation $invocation Object containing information on a mocked or stubbed method which was invoked + */ + public function invoked(BaseInvocation $invocation); + + /** + * Checks if the invocation $invocation matches the current rules. If it does + * the matcher will get the invoked() method called which should check if an + * expectation is met. + * + * @param BaseInvocation $invocation Object containing information on a mocked or stubbed method which was invoked + * + * @return bool + */ + public function matches(BaseInvocation $invocation); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Invocation matcher which checks if a method has been invoked at least + * N times. + */ +class InvokedAtLeastCount extends InvokedRecorder +{ + /** + * @var int + */ + private $requiredInvocations; + + /** + * @param int $requiredInvocations + */ + public function __construct($requiredInvocations) + { + $this->requiredInvocations = $requiredInvocations; + } + + public function toString(): string + { + return 'invoked at least ' . $this->requiredInvocations . ' times'; + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify(): void + { + $count = $this->getInvocationCount(); + + if ($count < $this->requiredInvocations) { + throw new ExpectationFailedException( + 'Expected invocation at least ' . $this->requiredInvocations . + ' times but it occurred ' . $count . ' time(s).' + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; + +class DeferredError extends StatelessInvocation +{ + /** + * @var \Throwable + */ + private $exception; + + public function __construct(\Throwable $exception) + { + $this->exception = $exception; + } + + public function verify(): void + { + throw $this->exception; + } + + public function toString(): string + { + return ''; + } + + public function matches(BaseInvocation $invocation): bool + { + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Matcher; + +use PHPUnit\Framework\ExpectationFailedException; + +/** + * Invocation matcher which checks if a method has been invoked at least + * N times. + */ +class InvokedAtMostCount extends InvokedRecorder +{ + /** + * @var int + */ + private $allowedInvocations; + + /** + * @param int $allowedInvocations + */ + public function __construct($allowedInvocations) + { + $this->allowedInvocations = $allowedInvocations; + } + + public function toString(): string + { + return 'invoked at most ' . $this->allowedInvocations . ' times'; + } + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function verify(): void + { + $count = $this->getInvocationCount(); + + if ($count > $this->allowedInvocations) { + throw new ExpectationFailedException( + 'Expected invocation at most ' . $this->allowedInvocations . + ' times but it occurred ' . $count . ' time(s).' + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\TestCase; + +/** + * Implementation of the Builder pattern for Mock objects. + */ +class MockBuilder +{ + /** + * @var TestCase + */ + private $testCase; + + /** + * @var string + */ + private $type; + + /** + * @var array + */ + private $methods = []; + + /** + * @var array + */ + private $methodsExcept = []; + + /** + * @var string + */ + private $mockClassName = ''; + + /** + * @var array + */ + private $constructorArgs = []; + + /** + * @var bool + */ + private $originalConstructor = true; + + /** + * @var bool + */ + private $originalClone = true; + + /** + * @var bool + */ + private $autoload = true; + + /** + * @var bool + */ + private $cloneArguments = false; + + /** + * @var bool + */ + private $callOriginalMethods = false; + + /** + * @var object + */ + private $proxyTarget; + + /** + * @var bool + */ + private $allowMockingUnknownTypes = true; + + /** + * @var bool + */ + private $returnValueGeneration = true; + + /** + * @var Generator + */ + private $generator; + + /** + * @param array|string $type + */ + public function __construct(TestCase $testCase, $type) + { + $this->testCase = $testCase; + $this->type = $type; + $this->generator = new Generator; + } + + /** + * Creates a mock object using a fluent interface. + * + * @return MockObject + */ + public function getMock() + { + $object = $this->generator->getMock( + $this->type, + $this->methods, + $this->constructorArgs, + $this->mockClassName, + $this->originalConstructor, + $this->originalClone, + $this->autoload, + $this->cloneArguments, + $this->callOriginalMethods, + $this->proxyTarget, + $this->allowMockingUnknownTypes, + $this->returnValueGeneration + ); + + $this->testCase->registerMockObject($object); + + return $object; + } + + /** + * Creates a mock object for an abstract class using a fluent interface. + * + * @return MockObject + */ + public function getMockForAbstractClass() + { + $object = $this->generator->getMockForAbstractClass( + $this->type, + $this->constructorArgs, + $this->mockClassName, + $this->originalConstructor, + $this->originalClone, + $this->autoload, + $this->methods, + $this->cloneArguments + ); + + $this->testCase->registerMockObject($object); + + return $object; + } + + /** + * Creates a mock object for a trait using a fluent interface. + * + * @return MockObject + */ + public function getMockForTrait() + { + $object = $this->generator->getMockForTrait( + $this->type, + $this->constructorArgs, + $this->mockClassName, + $this->originalConstructor, + $this->originalClone, + $this->autoload, + $this->methods, + $this->cloneArguments + ); + + $this->testCase->registerMockObject($object); + + return $object; + } + + /** + * Specifies the subset of methods to mock. Default is to mock none of them. + * + * @return MockBuilder + */ + public function setMethods(array $methods = null) + { + $this->methods = $methods; + + return $this; + } + + /** + * Specifies the subset of methods to not mock. Default is to mock all of them. + * + * @return MockBuilder + */ + public function setMethodsExcept(array $methods = []) + { + $this->methodsExcept = $methods; + + $this->setMethods( + \array_diff( + $this->generator->getClassMethods($this->type), + $this->methodsExcept + ) + ); + + return $this; + } + + /** + * Specifies the arguments for the constructor. + * + * @return MockBuilder + */ + public function setConstructorArgs(array $args) + { + $this->constructorArgs = $args; + + return $this; + } + + /** + * Specifies the name for the mock class. + * + * @param string $name + * + * @return MockBuilder + */ + public function setMockClassName($name) + { + $this->mockClassName = $name; + + return $this; + } + + /** + * Disables the invocation of the original constructor. + * + * @return MockBuilder + */ + public function disableOriginalConstructor() + { + $this->originalConstructor = false; + + return $this; + } + + /** + * Enables the invocation of the original constructor. + * + * @return MockBuilder + */ + public function enableOriginalConstructor() + { + $this->originalConstructor = true; + + return $this; + } + + /** + * Disables the invocation of the original clone constructor. + * + * @return MockBuilder + */ + public function disableOriginalClone() + { + $this->originalClone = false; + + return $this; + } + + /** + * Enables the invocation of the original clone constructor. + * + * @return MockBuilder + */ + public function enableOriginalClone() + { + $this->originalClone = true; + + return $this; + } + + /** + * Disables the use of class autoloading while creating the mock object. + * + * @return MockBuilder + */ + public function disableAutoload() + { + $this->autoload = false; + + return $this; + } + + /** + * Enables the use of class autoloading while creating the mock object. + * + * @return MockBuilder + */ + public function enableAutoload() + { + $this->autoload = true; + + return $this; + } + + /** + * Disables the cloning of arguments passed to mocked methods. + * + * @return MockBuilder + */ + public function disableArgumentCloning() + { + $this->cloneArguments = false; + + return $this; + } + + /** + * Enables the cloning of arguments passed to mocked methods. + * + * @return MockBuilder + */ + public function enableArgumentCloning() + { + $this->cloneArguments = true; + + return $this; + } + + /** + * Enables the invocation of the original methods. + * + * @return MockBuilder + */ + public function enableProxyingToOriginalMethods() + { + $this->callOriginalMethods = true; + + return $this; + } + + /** + * Disables the invocation of the original methods. + * + * @return MockBuilder + */ + public function disableProxyingToOriginalMethods() + { + $this->callOriginalMethods = false; + $this->proxyTarget = null; + + return $this; + } + + /** + * Sets the proxy target. + * + * @param object $object + * + * @return MockBuilder + */ + public function setProxyTarget($object) + { + $this->proxyTarget = $object; + + return $this; + } + + /** + * @return MockBuilder + */ + public function allowMockingUnknownTypes() + { + $this->allowMockingUnknownTypes = true; + + return $this; + } + + /** + * @return MockBuilder + */ + public function disallowMockingUnknownTypes() + { + $this->allowMockingUnknownTypes = false; + + return $this; + } + + /** + * @return MockBuilder + */ + public function enableAutoReturnValueGeneration() + { + $this->returnValueGeneration = true; + + return $this; + } + + /** + * @return MockBuilder + */ + public function disableAutoReturnValueGeneration() + { + $this->returnValueGeneration = false; + + return $this; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; + +/** + * Stubs a method by returning an argument that was passed to the mocked method. + */ +class ReturnArgument implements Stub +{ + /** + * @var int + */ + private $argumentIndex; + + public function __construct($argumentIndex) + { + $this->argumentIndex = $argumentIndex; + } + + public function invoke(Invocation $invocation) + { + if (isset($invocation->getParameters()[$this->argumentIndex])) { + return $invocation->getParameters()[$this->argumentIndex]; + } + } + + public function toString(): string + { + return \sprintf('return argument #%d', $this->argumentIndex); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; +use SebastianBergmann\Exporter\Exporter; + +/** + * Stubs a method by returning a user-defined stack of values. + */ +class ConsecutiveCalls implements Stub +{ + /** + * @var array + */ + private $stack; + + /** + * @var mixed + */ + private $value; + + public function __construct(array $stack) + { + $this->stack = $stack; + } + + public function invoke(Invocation $invocation) + { + $this->value = \array_shift($this->stack); + + if ($this->value instanceof Stub) { + $this->value = $this->value->invoke($invocation); + } + + return $this->value; + } + + public function toString(): string + { + $exporter = new Exporter; + + return \sprintf( + 'return user-specified value %s', + $exporter->export($this->value) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Matcher\Invocation; + +/** + * Stubs a method by returning a user-defined value. + */ +interface MatcherCollection +{ + /** + * Adds a new matcher to the collection which can be used as an expectation + * or a stub. + * + * @param Invocation $matcher Matcher for invocations to mock objects + */ + public function addMatcher(Invocation $matcher); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; +use SebastianBergmann\Exporter\Exporter; + +/** + * Stubs a method by raising a user-defined exception. + */ +class Exception implements Stub +{ + private $exception; + + public function __construct(\Throwable $exception) + { + $this->exception = $exception; + } + + public function invoke(Invocation $invocation): void + { + throw $this->exception; + } + + public function toString(): string + { + $exporter = new Exporter; + + return \sprintf( + 'raise user-specified exception %s', + $exporter->export($this->exception) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; + +/** + * Stubs a method by returning a value from a map. + */ +class ReturnValueMap implements Stub +{ + /** + * @var array + */ + private $valueMap; + + public function __construct(array $valueMap) + { + $this->valueMap = $valueMap; + } + + public function invoke(Invocation $invocation) + { + $parameterCount = \count($invocation->getParameters()); + + foreach ($this->valueMap as $map) { + if (!\is_array($map) || $parameterCount !== (\count($map) - 1)) { + continue; + } + + $return = \array_pop($map); + + if ($invocation->getParameters() === $map) { + return $return; + } + } + } + + public function toString(): string + { + return 'return value from a map'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; +use SebastianBergmann\Exporter\Exporter; + +/** + * Stubs a method by returning a user-defined reference to a value. + */ +class ReturnReference implements Stub +{ + /** + * @var mixed + */ + private $reference; + + public function __construct(&$reference) + { + $this->reference = &$reference; + } + + public function invoke(Invocation $invocation) + { + return $this->reference; + } + + public function toString(): string + { + $exporter = new Exporter; + + return \sprintf( + 'return user-specified reference %s', + $exporter->export($this->reference) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; +use SebastianBergmann\Exporter\Exporter; + +/** + * Stubs a method by returning a user-defined value. + */ +class ReturnStub implements Stub +{ + /** + * @var mixed + */ + private $value; + + public function __construct($value) + { + $this->value = $value; + } + + public function invoke(Invocation $invocation) + { + return $this->value; + } + + public function toString(): string + { + $exporter = new Exporter; + + return \sprintf( + 'return user-specified value %s', + $exporter->export($this->value) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Stub; + +class ReturnCallback implements Stub +{ + private $callback; + + public function __construct($callback) + { + $this->callback = $callback; + } + + public function invoke(Invocation $invocation) + { + return \call_user_func_array($this->callback, $invocation->getParameters()); + } + + public function toString(): string + { + if (\is_array($this->callback)) { + if (\is_object($this->callback[0])) { + $class = \get_class($this->callback[0]); + $type = '->'; + } else { + $class = $this->callback[0]; + $type = '::'; + } + + return \sprintf( + 'return result of user defined callback %s%s%s() with the ' . + 'passed arguments', + $class, + $type, + $this->callback[1] + ); + } + + return 'return result of user defined callback ' . $this->callback . + ' with the passed arguments'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Stub; + +use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\Invocation\ObjectInvocation; +use PHPUnit\Framework\MockObject\RuntimeException; +use PHPUnit\Framework\MockObject\Stub; + +/** + * Stubs a method by returning the current object. + */ +class ReturnSelf implements Stub +{ + public function invoke(Invocation $invocation) + { + if (!$invocation instanceof ObjectInvocation) { + throw new RuntimeException( + 'The current object can only be returned when mocking an ' . + 'object, not a static class.' + ); + } + + return $invocation->getObject(); + } + + public function toString(): string + { + return 'return the current object'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +final class MockMethodSet +{ + /** + * @var MockMethod[] + */ + private $methods = []; + + public function addMethods(MockMethod ...$methods): void + { + foreach ($methods as $method) { + $this->methods[\strtolower($method->getName())] = $method; + } + } + + public function asArray(): array + { + return \array_values($this->methods); + } + + public function hasMethod(string $methodName): bool + { + return \array_key_exists(\strtolower($methodName), $this->methods); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount; +use PHPUnit\Framework\MockObject\Matcher\AnyParameters; +use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount; +use PHPUnit\Framework\MockObject\Matcher\MethodName; +use PHPUnit\Framework\MockObject\Matcher\Parameters; +use PHPUnit\Framework\TestFailure; + +/** + * Main matcher which defines a full expectation using method, parameter and + * invocation matchers. + * This matcher encapsulates all the other matchers and allows the builder to + * set the specific matchers when the appropriate methods are called (once(), + * where() etc.). + * + * All properties are public so that they can easily be accessed by the builder. + */ +class Matcher implements MatcherInvocation +{ + /** + * @var MatcherInvocation + */ + private $invocationMatcher; + + /** + * @var mixed + */ + private $afterMatchBuilderId; + + /** + * @var bool + */ + private $afterMatchBuilderIsInvoked = false; + + /** + * @var MethodName + */ + private $methodNameMatcher; + + /** + * @var Parameters + */ + private $parametersMatcher; + + /** + * @var Stub + */ + private $stub; + + public function __construct(MatcherInvocation $invocationMatcher) + { + $this->invocationMatcher = $invocationMatcher; + } + + public function hasMatchers(): bool + { + return $this->invocationMatcher !== null && !$this->invocationMatcher instanceof AnyInvokedCount; + } + + public function hasMethodNameMatcher(): bool + { + return $this->methodNameMatcher !== null; + } + + public function getMethodNameMatcher(): MethodName + { + return $this->methodNameMatcher; + } + + public function setMethodNameMatcher(MethodName $matcher): void + { + $this->methodNameMatcher = $matcher; + } + + public function hasParametersMatcher(): bool + { + return $this->parametersMatcher !== null; + } + + public function getParametersMatcher(): Parameters + { + return $this->parametersMatcher; + } + + public function setParametersMatcher($matcher): void + { + $this->parametersMatcher = $matcher; + } + + public function setStub($stub): void + { + $this->stub = $stub; + } + + public function setAfterMatchBuilderId($id): void + { + $this->afterMatchBuilderId = $id; + } + + /** + * @throws \Exception + * @throws RuntimeException + * @throws ExpectationFailedException + */ + public function invoked(Invocation $invocation) + { + if ($this->invocationMatcher === null) { + throw new RuntimeException( + 'No invocation matcher is set' + ); + } + + if ($this->methodNameMatcher === null) { + throw new RuntimeException('No method matcher is set'); + } + + if ($this->afterMatchBuilderId !== null) { + $builder = $invocation->getObject() + ->__phpunit_getInvocationMocker() + ->lookupId($this->afterMatchBuilderId); + + if (!$builder) { + throw new RuntimeException( + \sprintf( + 'No builder found for match builder identification <%s>', + $this->afterMatchBuilderId + ) + ); + } + + $matcher = $builder->getMatcher(); + + if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) { + $this->afterMatchBuilderIsInvoked = true; + } + } + + $this->invocationMatcher->invoked($invocation); + + try { + if ($this->parametersMatcher !== null && + !$this->parametersMatcher->matches($invocation)) { + $this->parametersMatcher->verify(); + } + } catch (ExpectationFailedException $e) { + throw new ExpectationFailedException( + \sprintf( + "Expectation failed for %s when %s\n%s", + $this->methodNameMatcher->toString(), + $this->invocationMatcher->toString(), + $e->getMessage() + ), + $e->getComparisonFailure() + ); + } + + if ($this->stub) { + return $this->stub->invoke($invocation); + } + + return $invocation->generateReturnValue(); + } + + /** + * @throws RuntimeException + * @throws ExpectationFailedException + * + * @return bool + */ + public function matches(Invocation $invocation) + { + if ($this->afterMatchBuilderId !== null) { + $builder = $invocation->getObject() + ->__phpunit_getInvocationMocker() + ->lookupId($this->afterMatchBuilderId); + + if (!$builder) { + throw new RuntimeException( + \sprintf( + 'No builder found for match builder identification <%s>', + $this->afterMatchBuilderId + ) + ); + } + + $matcher = $builder->getMatcher(); + + if (!$matcher) { + return false; + } + + if (!$matcher->invocationMatcher->hasBeenInvoked()) { + return false; + } + } + + if ($this->invocationMatcher === null) { + throw new RuntimeException( + 'No invocation matcher is set' + ); + } + + if ($this->methodNameMatcher === null) { + throw new RuntimeException('No method matcher is set'); + } + + if (!$this->invocationMatcher->matches($invocation)) { + return false; + } + + try { + if (!$this->methodNameMatcher->matches($invocation)) { + return false; + } + } catch (ExpectationFailedException $e) { + throw new ExpectationFailedException( + \sprintf( + "Expectation failed for %s when %s\n%s", + $this->methodNameMatcher->toString(), + $this->invocationMatcher->toString(), + $e->getMessage() + ), + $e->getComparisonFailure() + ); + } + + return true; + } + + /** + * @throws RuntimeException + * @throws ExpectationFailedException + */ + public function verify(): void + { + if ($this->invocationMatcher === null) { + throw new RuntimeException( + 'No invocation matcher is set' + ); + } + + if ($this->methodNameMatcher === null) { + throw new RuntimeException('No method matcher is set'); + } + + try { + $this->invocationMatcher->verify(); + + if ($this->parametersMatcher === null) { + $this->parametersMatcher = new AnyParameters; + } + + $invocationIsAny = $this->invocationMatcher instanceof AnyInvokedCount; + $invocationIsNever = $this->invocationMatcher instanceof InvokedCount && $this->invocationMatcher->isNever(); + + if (!$invocationIsAny && !$invocationIsNever) { + $this->parametersMatcher->verify(); + } + } catch (ExpectationFailedException $e) { + throw new ExpectationFailedException( + \sprintf( + "Expectation failed for %s when %s.\n%s", + $this->methodNameMatcher->toString(), + $this->invocationMatcher->toString(), + TestFailure::exceptionToString($e) + ) + ); + } + } + + public function toString(): string + { + $list = []; + + if ($this->invocationMatcher !== null) { + $list[] = $this->invocationMatcher->toString(); + } + + if ($this->methodNameMatcher !== null) { + $list[] = 'where ' . $this->methodNameMatcher->toString(); + } + + if ($this->parametersMatcher !== null) { + $list[] = 'and ' . $this->parametersMatcher->toString(); + } + + if ($this->afterMatchBuilderId !== null) { + $list[] = 'after ' . $this->afterMatchBuilderId; + } + + if ($this->stub !== null) { + $list[] = 'will ' . $this->stub->toString(); + } + + return \implode(' ', $list); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\MockObject\Matcher; +use PHPUnit\Framework\MockObject\Matcher\Invocation; +use PHPUnit\Framework\MockObject\RuntimeException; +use PHPUnit\Framework\MockObject\Stub; +use PHPUnit\Framework\MockObject\Stub\MatcherCollection; + +/** + * Builder for mocked or stubbed invocations. + * + * Provides methods for building expectations without having to resort to + * instantiating the various matchers manually. These methods also form a + * more natural way of reading the expectation. This class should be together + * with the test case PHPUnit\Framework\MockObject\TestCase. + */ +class InvocationMocker implements MethodNameMatch +{ + /** + * @var MatcherCollection + */ + private $collection; + + /** + * @var Matcher + */ + private $matcher; + + /** + * @var string[] + */ + private $configurableMethods; + + public function __construct(MatcherCollection $collection, Invocation $invocationMatcher, array $configurableMethods) + { + $this->collection = $collection; + $this->matcher = new Matcher($invocationMatcher); + + $this->collection->addMatcher($this->matcher); + + $this->configurableMethods = $configurableMethods; + } + + /** + * @return Matcher + */ + public function getMatcher() + { + return $this->matcher; + } + + /** + * @return InvocationMocker + */ + public function id($id) + { + $this->collection->registerId($id, $this); + + return $this; + } + + /** + * @return InvocationMocker + */ + public function will(Stub $stub) + { + $this->matcher->setStub($stub); + + return $this; + } + + /** + * @return InvocationMocker + */ + public function willReturn($value, ...$nextValues) + { + if (\count($nextValues) === 0) { + $stub = new Stub\ReturnStub($value); + } else { + $stub = new Stub\ConsecutiveCalls( + \array_merge([$value], $nextValues) + ); + } + + return $this->will($stub); + } + + /** + * @param mixed $reference + * + * @return InvocationMocker + */ + public function willReturnReference(&$reference) + { + $stub = new Stub\ReturnReference($reference); + + return $this->will($stub); + } + + /** + * @return InvocationMocker + */ + public function willReturnMap(array $valueMap) + { + $stub = new Stub\ReturnValueMap($valueMap); + + return $this->will($stub); + } + + /** + * @return InvocationMocker + */ + public function willReturnArgument($argumentIndex) + { + $stub = new Stub\ReturnArgument($argumentIndex); + + return $this->will($stub); + } + + /** + * @param callable $callback + * + * @return InvocationMocker + */ + public function willReturnCallback($callback) + { + $stub = new Stub\ReturnCallback($callback); + + return $this->will($stub); + } + + /** + * @return InvocationMocker + */ + public function willReturnSelf() + { + $stub = new Stub\ReturnSelf; + + return $this->will($stub); + } + + /** + * @return InvocationMocker + */ + public function willReturnOnConsecutiveCalls(...$values) + { + $stub = new Stub\ConsecutiveCalls($values); + + return $this->will($stub); + } + + /** + * @return InvocationMocker + */ + public function willThrowException(\Exception $exception) + { + $stub = new Stub\Exception($exception); + + return $this->will($stub); + } + + /** + * @return InvocationMocker + */ + public function after($id) + { + $this->matcher->setAfterMatchBuilderId($id); + + return $this; + } + + /** + * @param array ...$arguments + * + * @throws RuntimeException + * + * @return InvocationMocker + */ + public function with(...$arguments) + { + $this->canDefineParameters(); + + $this->matcher->setParametersMatcher(new Matcher\Parameters($arguments)); + + return $this; + } + + /** + * @param array ...$arguments + * + * @throws RuntimeException + * + * @return InvocationMocker + */ + public function withConsecutive(...$arguments) + { + $this->canDefineParameters(); + + $this->matcher->setParametersMatcher(new Matcher\ConsecutiveParameters($arguments)); + + return $this; + } + + /** + * @throws RuntimeException + * + * @return InvocationMocker + */ + public function withAnyParameters() + { + $this->canDefineParameters(); + + $this->matcher->setParametersMatcher(new Matcher\AnyParameters); + + return $this; + } + + /** + * @param Constraint|string $constraint + * + * @throws RuntimeException + * + * @return InvocationMocker + */ + public function method($constraint) + { + if ($this->matcher->hasMethodNameMatcher()) { + throw new RuntimeException( + 'Method name matcher is already defined, cannot redefine' + ); + } + + if (\is_string($constraint) && !\in_array(\strtolower($constraint), $this->configurableMethods, true)) { + throw new RuntimeException( + \sprintf( + 'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', + $constraint + ) + ); + } + + $this->matcher->setMethodNameMatcher(new Matcher\MethodName($constraint)); + + return $this; + } + + /** + * Validate that a parameters matcher can be defined, throw exceptions otherwise. + * + * @throws RuntimeException + */ + private function canDefineParameters(): void + { + if (!$this->matcher->hasMethodNameMatcher()) { + throw new RuntimeException( + 'Method name matcher is not defined, cannot define parameter ' . + 'matcher without one' + ); + } + + if ($this->matcher->hasParametersMatcher()) { + throw new RuntimeException( + 'Parameter matcher is already defined, cannot redefine' + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use PHPUnit\Framework\MockObject\Stub as BaseStub; + +/** + * Builder interface for stubs which are actions replacing an invocation. + */ +interface Stub extends Identity +{ + /** + * Stubs the matching method with the stub object $stub. Any invocations of + * the matched method will now be handled by the stub instead. + * + * @return Identity + */ + public function will(BaseStub $stub); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * Interface for builders which can register builders with a given identification. + * + * This interface relates to Identity. + */ +interface NamespaceMatch +{ + /** + * Looks up the match builder with identification $id and returns it. + * + * @param string $id The identification of the match builder + * + * @return Match + */ + public function lookupId($id); + + /** + * Registers the match builder $builder with the identification $id. The + * builder can later be looked up using lookupId() to figure out if it + * has been invoked. + * + * @param string $id The identification of the match builder + * @param Match $builder The builder which is being registered + */ + public function registerId($id, Match $builder); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * Builder interface for unique identifiers. + * + * Defines the interface for recording unique identifiers. The identifiers + * can be used to define the invocation order of expectations. The expectation + * is recorded using id() and then defined in order using + * PHPUnit\Framework\MockObject\Builder\Match::after(). + */ +interface Identity +{ + /** + * Sets the identification of the expectation to $id. + * + * @note The identifier is unique per mock object. + * + * @param string $id unique identification of expectation + */ + public function id($id); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use PHPUnit\Framework\MockObject\Matcher\AnyParameters; + +/** + * Builder interface for parameter matchers. + */ +interface ParametersMatch extends Match +{ + /** + * Sets the parameters to match for, each parameter to this function will + * be part of match. To perform specific matches or constraints create a + * new PHPUnit\Framework\Constraint\Constraint and use it for the parameter. + * If the parameter value is not a constraint it will use the + * PHPUnit\Framework\Constraint\IsEqual for the value. + * + * Some examples: + * + * // match first parameter with value 2 + * $b->with(2); + * // match first parameter with value 'smock' and second identical to 42 + * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); + * + * + * @return ParametersMatch + */ + public function with(...$arguments); + + /** + * Sets a matcher which allows any kind of parameters. + * + * Some examples: + * + * // match any number of parameters + * $b->withAnyParameters(); + * + * + * @return AnyParameters + */ + public function withAnyParameters(); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * Builder interface for invocation order matches. + */ +interface Match extends Stub +{ + /** + * Defines the expectation which must occur before the current is valid. + * + * @param string $id the identification of the expectation that should + * occur before this one + * + * @return Stub + */ + public function after($id); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +/** + * Builder interface for matcher of method names. + */ +interface MethodNameMatch extends ParametersMatch +{ + /** + * Adds a new method name match and returns the parameter match object for + * further matching possibilities. + * + * @param \PHPUnit\Framework\Constraint\Constraint $name Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual + * + * @return ParametersMatch + */ + public function method($name); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException; +use Doctrine\Instantiator\Instantiator; +use Iterator; +use IteratorAggregate; +use PHPUnit\Framework\Exception; +use PHPUnit\Util\InvalidArgumentHelper; +use ReflectionClass; +use ReflectionMethod; +use SoapClient; +use Text_Template; +use Traversable; + +/** + * Mock Object Code Generator + */ +class Generator +{ + /** + * @var array + */ + private const BLACKLISTED_METHOD_NAMES = [ + '__CLASS__' => true, + '__DIR__' => true, + '__FILE__' => true, + '__FUNCTION__' => true, + '__LINE__' => true, + '__METHOD__' => true, + '__NAMESPACE__' => true, + '__TRAIT__' => true, + '__clone' => true, + '__halt_compiler' => true, + ]; + + /** + * @var array + */ + private static $cache = []; + + /** + * @var Text_Template[] + */ + private static $templates = []; + + /** + * Returns a mock object for the specified class. + * + * @param string|string[] $type + * @param array $methods + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * @param object $proxyTarget + * @param bool $allowMockingUnknownTypes + * @param bool $returnValueGeneration + * + * @throws Exception + * @throws RuntimeException + * @throws \PHPUnit\Framework\Exception + * @throws \ReflectionException + * + * @return MockObject + */ + public function getMock($type, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null, $allowMockingUnknownTypes = true, $returnValueGeneration = true) + { + if (!\is_array($type) && !\is_string($type)) { + throw InvalidArgumentHelper::factory(1, 'array or string'); + } + + if (!\is_string($mockClassName)) { + throw InvalidArgumentHelper::factory(4, 'string'); + } + + if (!\is_array($methods) && null !== $methods) { + throw InvalidArgumentHelper::factory(2, 'array', $methods); + } + + if ($type === 'Traversable' || $type === '\\Traversable') { + $type = 'Iterator'; + } + + if (\is_array($type)) { + $type = \array_unique( + \array_map( + function ($type) { + if ($type === 'Traversable' || + $type === '\\Traversable' || + $type === '\\Iterator') { + return 'Iterator'; + } + + return $type; + }, + $type + ) + ); + } + + if (!$allowMockingUnknownTypes) { + if (\is_array($type)) { + foreach ($type as $_type) { + if (!\class_exists($_type, $callAutoload) && + !\interface_exists($_type, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock class or interface "%s" which does not exist', + $_type + ) + ); + } + } + } else { + if (!\class_exists($type, $callAutoload) && + !\interface_exists($type, $callAutoload) + ) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock class or interface "%s" which does not exist', + $type + ) + ); + } + } + } + + if (null !== $methods) { + foreach ($methods as $method) { + if (!\preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock method with invalid name "%s"', + $method + ) + ); + } + } + + if ($methods !== \array_unique($methods)) { + throw new RuntimeException( + \sprintf( + 'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")', + \implode(', ', $methods), + \implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods)))) + ) + ); + } + } + + if ($mockClassName !== '' && \class_exists($mockClassName, false)) { + $reflect = new ReflectionClass($mockClassName); + + if (!$reflect->implementsInterface(MockObject::class)) { + throw new RuntimeException( + \sprintf( + 'Class "%s" already exists.', + $mockClassName + ) + ); + } + } + + if ($callOriginalConstructor === false && $callOriginalMethods === true) { + throw new RuntimeException( + 'Proxying to original methods requires invoking the original constructor' + ); + } + + $mock = $this->generate( + $type, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments, + $callOriginalMethods + ); + + return $this->getObject( + $mock['code'], + $mock['mockClassName'], + $type, + $callOriginalConstructor, + $callAutoload, + $arguments, + $callOriginalMethods, + $proxyTarget, + $returnValueGeneration + ); + } + + /** + * Returns a mock object for the specified abstract class with all abstract + * methods of the class mocked. Concrete methods to mock can be specified with + * the last parameter + * + * @param string $originalClassName + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @throws \ReflectionException + * @throws RuntimeException + * @throws Exception + * + * @return MockObject + */ + public function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true) + { + if (!\is_string($originalClassName)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + if (!\is_string($mockClassName)) { + throw InvalidArgumentHelper::factory(3, 'string'); + } + + if (\class_exists($originalClassName, $callAutoload) || + \interface_exists($originalClassName, $callAutoload)) { + $reflector = new ReflectionClass($originalClassName); + $methods = $mockedMethods; + + foreach ($reflector->getMethods() as $method) { + if ($method->isAbstract() && !\in_array($method->getName(), $methods, true)) { + $methods[] = $method->getName(); + } + } + + if (empty($methods)) { + $methods = null; + } + + return $this->getMock( + $originalClassName, + $methods, + $arguments, + $mockClassName, + $callOriginalConstructor, + $callOriginalClone, + $callAutoload, + $cloneArguments + ); + } + + throw new RuntimeException( + \sprintf('Class "%s" does not exist.', $originalClassName) + ); + } + + /** + * Returns a mock object for the specified trait with all abstract methods + * of the trait mocked. Concrete methods to mock can be specified with the + * `$mockedMethods` parameter. + * + * @param string $traitName + * @param string $mockClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param array $mockedMethods + * @param bool $cloneArguments + * + * @throws \ReflectionException + * @throws RuntimeException + * @throws Exception + * + * @return MockObject + */ + public function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true) + { + if (!\is_string($traitName)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + if (!\is_string($mockClassName)) { + throw InvalidArgumentHelper::factory(3, 'string'); + } + + if (!\trait_exists($traitName, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Trait "%s" does not exist.', + $traitName + ) + ); + } + + $className = $this->generateClassName( + $traitName, + '', + 'Trait_' + ); + + $classTemplate = $this->getTemplate('trait_class.tpl'); + + $classTemplate->setVar( + [ + 'prologue' => 'abstract ', + 'class_name' => $className['className'], + 'trait_name' => $traitName, + ] + ); + + $this->evalClass( + $classTemplate->render(), + $className['className'] + ); + + return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); + } + + /** + * Returns an object for the specified trait. + * + * @param string $traitName + * @param string $traitClassName + * @param bool $callOriginalConstructor + * @param bool $callOriginalClone + * @param bool $callAutoload + * + * @throws \ReflectionException + * @throws RuntimeException + * @throws Exception + * + * @return object + */ + public function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true) + { + if (!\is_string($traitName)) { + throw InvalidArgumentHelper::factory(1, 'string'); + } + + if (!\is_string($traitClassName)) { + throw InvalidArgumentHelper::factory(3, 'string'); + } + + if (!\trait_exists($traitName, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Trait "%s" does not exist.', + $traitName + ) + ); + } + + $className = $this->generateClassName( + $traitName, + $traitClassName, + 'Trait_' + ); + + $classTemplate = $this->getTemplate('trait_class.tpl'); + + $classTemplate->setVar( + [ + 'prologue' => '', + 'class_name' => $className['className'], + 'trait_name' => $traitName, + ] + ); + + return $this->getObject($classTemplate->render(), $className['className']); + } + + /** + * @param array|string $type + * @param array $methods + * @param string $mockClassName + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * + * @throws \ReflectionException + * @throws \PHPUnit\Framework\MockObject\RuntimeException + * + * @return array + */ + public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false) + { + if (\is_array($type)) { + \sort($type); + } + + if ($mockClassName !== '') { + return $this->generateMock( + $type, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments, + $callOriginalMethods + ); + } + $key = \md5( + \is_array($type) ? \implode('_', $type) : $type . + \serialize($methods) . + \serialize($callOriginalClone) . + \serialize($cloneArguments) . + \serialize($callOriginalMethods) + ); + + if (!isset(self::$cache[$key])) { + self::$cache[$key] = $this->generateMock( + $type, + $methods, + $mockClassName, + $callOriginalClone, + $callAutoload, + $cloneArguments, + $callOriginalMethods + ); + } + + return self::$cache[$key]; + } + + /** + * @param string $wsdlFile + * @param string $className + * + * @throws RuntimeException + * + * @return string + */ + public function generateClassFromWsdl($wsdlFile, $className, array $methods = [], array $options = []) + { + if (!\extension_loaded('soap')) { + throw new RuntimeException( + 'The SOAP extension is required to generate a mock object from WSDL.' + ); + } + + $options = \array_merge($options, ['cache_wsdl' => \WSDL_CACHE_NONE]); + $client = new SoapClient($wsdlFile, $options); + $_methods = \array_unique($client->__getFunctions()); + unset($client); + + \sort($_methods); + + $methodTemplate = $this->getTemplate('wsdl_method.tpl'); + $methodsBuffer = ''; + + foreach ($_methods as $method) { + \preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(/', $method, $matches, \PREG_OFFSET_CAPTURE); + $lastFunction = \array_pop($matches[0]); + $nameStart = $lastFunction[1]; + $nameEnd = $nameStart + \strlen($lastFunction[0]) - 1; + $name = \str_replace('(', '', $lastFunction[0]); + + if (empty($methods) || \in_array($name, $methods, true)) { + $args = \explode( + ',', + \str_replace(')', '', \substr($method, $nameEnd + 1)) + ); + + foreach (\range(0, \count($args) - 1) as $i) { + $args[$i] = \substr($args[$i], \strpos($args[$i], '$')); + } + + $methodTemplate->setVar( + [ + 'method_name' => $name, + 'arguments' => \implode(', ', $args), + ] + ); + + $methodsBuffer .= $methodTemplate->render(); + } + } + + $optionsBuffer = '['; + + foreach ($options as $key => $value) { + $optionsBuffer .= $key . ' => ' . $value; + } + + $optionsBuffer .= ']'; + + $classTemplate = $this->getTemplate('wsdl_class.tpl'); + $namespace = ''; + + if (\strpos($className, '\\') !== false) { + $parts = \explode('\\', $className); + $className = \array_pop($parts); + $namespace = 'namespace ' . \implode('\\', $parts) . ';' . "\n\n"; + } + + $classTemplate->setVar( + [ + 'namespace' => $namespace, + 'class_name' => $className, + 'wsdl' => $wsdlFile, + 'options' => $optionsBuffer, + 'methods' => $methodsBuffer, + ] + ); + + return $classTemplate->render(); + } + + /** + * @param string $className + * + * @throws \ReflectionException + * + * @return string[] + */ + public function getClassMethods($className): array + { + $class = new ReflectionClass($className); + $methods = []; + + foreach ($class->getMethods() as $method) { + if ($method->isPublic() || $method->isAbstract()) { + $methods[] = $method->getName(); + } + } + + return $methods; + } + + /** + * @throws \ReflectionException + * + * @return MockMethod[] + */ + public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments): array + { + $class = new ReflectionClass($className); + $methods = []; + + foreach ($class->getMethods() as $method) { + if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { + $methods[] = MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); + } + } + + return $methods; + } + + /** + * @param string $code + * @param string $className + * @param array|string $type + * @param bool $callOriginalConstructor + * @param bool $callAutoload + * @param bool $callOriginalMethods + * @param object $proxyTarget + * @param bool $returnValueGeneration + * + * @throws \ReflectionException + * @throws RuntimeException + * + * @return MockObject + */ + private function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = [], $callOriginalMethods = false, $proxyTarget = null, $returnValueGeneration = true) + { + $this->evalClass($code, $className); + + if ($callOriginalConstructor && + \is_string($type) && + !\interface_exists($type, $callAutoload)) { + if (\count($arguments) === 0) { + $object = new $className; + } else { + $class = new ReflectionClass($className); + $object = $class->newInstanceArgs($arguments); + } + } else { + try { + $instantiator = new Instantiator; + $object = $instantiator->instantiate($className); + } catch (InstantiatorException $exception) { + throw new RuntimeException($exception->getMessage()); + } + } + + if ($callOriginalMethods) { + if (!\is_object($proxyTarget)) { + if (\count($arguments) === 0) { + $proxyTarget = new $type; + } else { + $class = new ReflectionClass($type); + $proxyTarget = $class->newInstanceArgs($arguments); + } + } + + $object->__phpunit_setOriginalObject($proxyTarget); + } + + if ($object instanceof MockObject) { + $object->__phpunit_setReturnValueGeneration($returnValueGeneration); + } + + return $object; + } + + /** + * @param string $code + * @param string $className + */ + private function evalClass($code, $className): void + { + if (!\class_exists($className, false)) { + eval($code); + } + } + + /** + * @param array|string $type + * @param null|array $explicitMethods + * @param string $mockClassName + * @param bool $callOriginalClone + * @param bool $callAutoload + * @param bool $cloneArguments + * @param bool $callOriginalMethods + * + * @throws \InvalidArgumentException + * @throws \ReflectionException + * @throws RuntimeException + * + * @return array + */ + private function generateMock($type, $explicitMethods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods) + { + $classTemplate = $this->getTemplate('mocked_class.tpl'); + + $additionalInterfaces = []; + $cloneTemplate = ''; + $isClass = false; + $isInterface = false; + $class = null; + $mockMethods = new MockMethodSet; + + if (\is_array($type)) { + $interfaceMethods = []; + + foreach ($type as $_type) { + if (!\interface_exists($_type, $callAutoload)) { + throw new RuntimeException( + \sprintf( + 'Interface "%s" does not exist.', + $_type + ) + ); + } + + $additionalInterfaces[] = $_type; + $typeClass = new ReflectionClass($_type); + + foreach ($this->getClassMethods($_type) as $method) { + if (\in_array($method, $interfaceMethods, true)) { + throw new RuntimeException( + \sprintf( + 'Duplicate method "%s" not allowed.', + $method + ) + ); + } + + $methodReflection = $typeClass->getMethod($method); + + if ($this->canMockMethod($methodReflection)) { + $mockMethods->addMethods( + MockMethod::fromReflection($methodReflection, $callOriginalMethods, $cloneArguments) + ); + + $interfaceMethods[] = $method; + } + } + } + + unset($interfaceMethods); + } + + $mockClassName = $this->generateClassName( + $type, + $mockClassName, + 'Mock_' + ); + + if (\class_exists($mockClassName['fullClassName'], $callAutoload)) { + $isClass = true; + } elseif (\interface_exists($mockClassName['fullClassName'], $callAutoload)) { + $isInterface = true; + } + + if (!$isClass && !$isInterface) { + $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n"; + + if (!empty($mockClassName['namespaceName'])) { + $prologue = 'namespace ' . $mockClassName['namespaceName'] . + " {\n\n" . $prologue . "}\n\n" . + "namespace {\n\n"; + + $epilogue = "\n\n}"; + } + + $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); + } else { + $class = new ReflectionClass($mockClassName['fullClassName']); + + if ($class->isFinal()) { + throw new RuntimeException( + \sprintf( + 'Class "%s" is declared "final" and cannot be mocked.', + $mockClassName['fullClassName'] + ) + ); + } + + // @see https://github.com/sebastianbergmann/phpunit/issues/2995 + if ($isInterface && $class->implementsInterface(\Throwable::class)) { + $additionalInterfaces[] = $class->getName(); + $isInterface = false; + + $mockClassName = $this->generateClassName( + \Exception::class, + '', + 'Mock_' + ); + + $class = new ReflectionClass($mockClassName['fullClassName']); + } + + // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 + if ($isInterface && $class->implementsInterface(Traversable::class) && + !$class->implementsInterface(Iterator::class) && + !$class->implementsInterface(IteratorAggregate::class)) { + $additionalInterfaces[] = Iterator::class; + + $mockMethods->addMethods( + ...$this->mockClassMethods(Iterator::class, $callOriginalMethods, $cloneArguments) + ); + } + + if ($class->hasMethod('__clone')) { + $cloneMethod = $class->getMethod('__clone'); + + if (!$cloneMethod->isFinal()) { + if ($callOriginalClone && !$isInterface) { + $cloneTemplate = $this->getTemplate('unmocked_clone.tpl'); + } else { + $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); + } + } + } else { + $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); + } + } + + if (\is_object($cloneTemplate)) { + $cloneTemplate = $cloneTemplate->render(); + } + + if ($explicitMethods === [] && + ($isClass || $isInterface)) { + $mockMethods->addMethods( + ...$this->mockClassMethods($mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments) + ); + } + + if (\is_array($explicitMethods)) { + foreach ($explicitMethods as $methodName) { + if ($class !== null && $class->hasMethod($methodName)) { + $method = $class->getMethod($methodName); + + if ($this->canMockMethod($method)) { + $mockMethods->addMethods( + MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments) + ); + } + } else { + $mockMethods->addMethods( + MockMethod::fromName( + $mockClassName['fullClassName'], + $methodName, + $cloneArguments + ) + ); + } + } + } + + $mockedMethods = ''; + $configurable = []; + + /** @var MockMethod $mockMethod */ + foreach ($mockMethods->asArray() as $mockMethod) { + $mockedMethods .= $mockMethod->generateCode(); + $configurable[] = \strtolower($mockMethod->getName()); + } + + $method = ''; + + if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { + $methodTemplate = $this->getTemplate('mocked_class_method.tpl'); + + $method = $methodTemplate->render(); + } + + $classTemplate->setVar( + [ + 'prologue' => $prologue ?? '', + 'epilogue' => $epilogue ?? '', + 'class_declaration' => $this->generateMockClassDeclaration( + $mockClassName, + $isInterface, + $additionalInterfaces + ), + 'clone' => $cloneTemplate, + 'mock_class_name' => $mockClassName['className'], + 'mocked_methods' => $mockedMethods, + 'method' => $method, + 'configurable' => '[' . \implode( + ', ', + \array_map( + function ($m) { + return '\'' . $m . '\''; + }, + $configurable + ) + ) . ']', + ] + ); + + return [ + 'code' => $classTemplate->render(), + 'mockClassName' => $mockClassName['className'], + ]; + } + + /** + * @param array|string $type + * @param string $className + * @param string $prefix + * + * @return array + */ + private function generateClassName($type, $className, $prefix) + { + if (\is_array($type)) { + $type = \implode('_', $type); + } + + if ($type[0] === '\\') { + $type = \substr($type, 1); + } + + $classNameParts = \explode('\\', $type); + + if (\count($classNameParts) > 1) { + $type = \array_pop($classNameParts); + $namespaceName = \implode('\\', $classNameParts); + $fullClassName = $namespaceName . '\\' . $type; + } else { + $namespaceName = ''; + $fullClassName = $type; + } + + if ($className === '') { + do { + $className = $prefix . $type . '_' . + \substr(\md5(\mt_rand()), 0, 8); + } while (\class_exists($className, false)); + } + + return [ + 'className' => $className, + 'originalClassName' => $type, + 'fullClassName' => $fullClassName, + 'namespaceName' => $namespaceName, + ]; + } + + /** + * @param bool $isInterface + * + * @return string + */ + private function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = []) + { + $buffer = 'class '; + + $additionalInterfaces[] = MockObject::class; + $interfaces = \implode(', ', $additionalInterfaces); + + if ($isInterface) { + $buffer .= \sprintf( + '%s implements %s', + $mockClassName['className'], + $interfaces + ); + + if (!\in_array($mockClassName['originalClassName'], $additionalInterfaces)) { + $buffer .= ', '; + + if (!empty($mockClassName['namespaceName'])) { + $buffer .= $mockClassName['namespaceName'] . '\\'; + } + + $buffer .= $mockClassName['originalClassName']; + } + } else { + $buffer .= \sprintf( + '%s extends %s%s implements %s', + $mockClassName['className'], + !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', + $mockClassName['originalClassName'], + $interfaces + ); + } + + return $buffer; + } + + /** + * @return bool + */ + private function canMockMethod(ReflectionMethod $method) + { + return !($method->isConstructor() || $method->isFinal() || $method->isPrivate() || $this->isMethodNameBlacklisted($method->getName())); + } + + /** + * Returns whether a method name is blacklisted + * + * @param string $name + * + * @return bool + */ + private function isMethodNameBlacklisted($name) + { + return isset(self::BLACKLISTED_METHOD_NAMES[$name]); + } + + /** + * @param string $template + * + * @throws \InvalidArgumentException + * + * @return Text_Template + */ + private function getTemplate($template) + { + $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; + + if (!isset(self::$templates[$filename])) { + self::$templates[$filename] = new Text_Template($filename); + } + + return self::$templates[$filename]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use PHPUnit\Framework\MockObject\Matcher\Invocation; + +/** + * Interface for all mock objects which are generated by + * MockBuilder. + * + * @method InvocationMocker method($constraint) + * + * @deprecated Use PHPUnit\Framework\MockObject\MockObject instead + */ +interface PHPUnit_Framework_MockObject_MockObject /*extends Verifiable*/ +{ + /** + * @return InvocationMocker + */ + public function __phpunit_setOriginalObject($originalObject); + + /** + * @return InvocationMocker + */ + public function __phpunit_getInvocationMocker(); + + /** + * Verifies that the current expectation is valid. If everything is OK the + * code should just return, if not it must throw an exception. + * + * @throws ExpectationFailedException + */ + public function __phpunit_verify(bool $unsetInvocationMocker = true); + + /** + * @return bool + */ + public function __phpunit_hasMatchers(); + + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration); + + /** + * Registers a new expectation in the mock object and returns the match + * object which can be infused with further details. + * + * @return InvocationMocker + */ + public function expects(Invocation $matcher); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class SkippedTestSuiteError extends AssertionFailedError implements SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +interface RiskyTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * An incomplete test case + */ +class IncompleteTestCase extends TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var bool + */ + protected $backupGlobals = false; + + /** + * @var bool + */ + protected $backupStaticAttributes = false; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * @var bool + */ + protected $useErrorHandler = false; + + /** + * @var bool + */ + protected $useOutputBuffering = false; + + public function __construct(string $className, string $methodName, string $message = '') + { + parent::__construct($className . '::' . $methodName); + + $this->message = $message; + } + + public function getMessage(): string + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function toString(): string + { + return $this->getName(); + } + + /** + * @throws Exception + */ + protected function runTest(): void + { + $this->markTestIncomplete($this->message); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use RecursiveIterator; + +/** + * Iterator for test suites. + */ +final class TestSuiteIterator implements RecursiveIterator +{ + /** + * @var int + */ + private $position; + + /** + * @var Test[] + */ + private $tests; + + public function __construct(TestSuite $testSuite) + { + $this->tests = $testSuite->tests(); + } + + /** + * Rewinds the Iterator to the first element. + */ + public function rewind(): void + { + $this->position = 0; + } + + /** + * Checks if there is a current element after calls to rewind() or next(). + */ + public function valid(): bool + { + return $this->position < \count($this->tests); + } + + /** + * Returns the key of the current element. + */ + public function key(): int + { + return $this->position; + } + + /** + * Returns the current element. + */ + public function current(): Test + { + return $this->valid() ? $this->tests[$this->position] : null; + } + + /** + * Moves forward to next element. + */ + public function next(): void + { + $this->position++; + } + + /** + * Returns the sub iterator for the current element. + */ + public function getChildren(): self + { + return new self( + $this->tests[$this->position] + ); + } + + /** + * Checks whether the current element has children. + */ + public function hasChildren(): bool + { + return $this->tests[$this->position] instanceof TestSuite; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * Interface for classes that can return a description of itself. + */ +interface SelfDescribing +{ + /** + * Returns a string representation of the object. + */ + public function toString(): string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use AssertionError; +use Countable; +use Error; +use PHPUnit\Framework\MockObject\Exception as MockObjectException; +use PHPUnit\Util\Blacklist; +use PHPUnit\Util\ErrorHandler; +use PHPUnit\Util\Printer; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException as OriginalCoveredCodeNotExecutedException; +use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; +use SebastianBergmann\CodeCoverage\MissingCoversAnnotationException as OriginalMissingCoversAnnotationException; +use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; +use SebastianBergmann\Invoker\Invoker; +use SebastianBergmann\Invoker\TimeoutException; +use SebastianBergmann\ResourceOperations\ResourceOperations; +use SebastianBergmann\Timer\Timer; +use Throwable; + +/** + * A TestResult collects the results of executing a test case. + */ +class TestResult implements Countable +{ + /** + * @var array + */ + protected $passed = []; + + /** + * @var TestFailure[] + */ + protected $errors = []; + + /** + * @var TestFailure[] + */ + protected $failures = []; + + /** + * @var TestFailure[] + */ + protected $warnings = []; + + /** + * @var TestFailure[] + */ + protected $notImplemented = []; + + /** + * @var TestFailure[] + */ + protected $risky = []; + + /** + * @var TestFailure[] + */ + protected $skipped = []; + + /** + * @var TestListener[] + */ + protected $listeners = []; + + /** + * @var int + */ + protected $runTests = 0; + + /** + * @var float + */ + protected $time = 0; + + /** + * @var TestSuite + */ + protected $topTestSuite; + + /** + * Code Coverage information. + * + * @var CodeCoverage + */ + protected $codeCoverage; + + /** + * @var bool + */ + protected $convertErrorsToExceptions = true; + + /** + * @var bool + */ + protected $stop = false; + + /** + * @var bool + */ + protected $stopOnError = false; + + /** + * @var bool + */ + protected $stopOnFailure = false; + + /** + * @var bool + */ + protected $stopOnWarning = false; + + /** + * @var bool + */ + protected $beStrictAboutTestsThatDoNotTestAnything = true; + + /** + * @var bool + */ + protected $beStrictAboutOutputDuringTests = false; + + /** + * @var bool + */ + protected $beStrictAboutTodoAnnotatedTests = false; + + /** + * @var bool + */ + protected $beStrictAboutResourceUsageDuringSmallTests = false; + + /** + * @var bool + */ + protected $enforceTimeLimit = false; + + /** + * @var int + */ + protected $timeoutForSmallTests = 1; + + /** + * @var int + */ + protected $timeoutForMediumTests = 10; + + /** + * @var int + */ + protected $timeoutForLargeTests = 60; + + /** + * @var bool + */ + protected $stopOnRisky = false; + + /** + * @var bool + */ + protected $stopOnIncomplete = false; + + /** + * @var bool + */ + protected $stopOnSkipped = false; + + /** + * @var bool + */ + protected $lastTestFailed = false; + + /** + * @var int + */ + private $defaultTimeLimit = 0; + + /** + * @var bool + */ + private $stopOnDefect = false; + + /** + * @var bool + */ + private $registerMockObjectsFromTestArgumentsRecursively = false; + + public static function isAnyCoverageRequired(TestCase $test) + { + $annotations = $test->getAnnotations(); + + // If any methods have covers, coverage must me generated + if (isset($annotations['method']['covers'])) { + return true; + } + + // If there are no explicit covers, and the test class is + // marked as covers nothing, all coverage can be skipped + if (isset($annotations['class']['coversNothing'])) { + return false; + } + + // Otherwise each test method can generate coverage + return true; + } + + /** + * Registers a TestListener. + */ + public function addListener(TestListener $listener): void + { + $this->listeners[] = $listener; + } + + /** + * Unregisters a TestListener. + */ + public function removeListener(TestListener $listener): void + { + foreach ($this->listeners as $key => $_listener) { + if ($listener === $_listener) { + unset($this->listeners[$key]); + } + } + } + + /** + * Flushes all flushable TestListeners. + */ + public function flushListeners(): void + { + foreach ($this->listeners as $listener) { + if ($listener instanceof Printer) { + $listener->flush(); + } + } + } + + /** + * Adds an error to the list of errors. + */ + public function addError(Test $test, Throwable $t, float $time): void + { + if ($t instanceof RiskyTest) { + $this->risky[] = new TestFailure($test, $t); + $notifyMethod = 'addRiskyTest'; + + if ($test instanceof TestCase) { + $test->markAsRisky(); + } + + if ($this->stopOnRisky || $this->stopOnDefect) { + $this->stop(); + } + } elseif ($t instanceof IncompleteTest) { + $this->notImplemented[] = new TestFailure($test, $t); + $notifyMethod = 'addIncompleteTest'; + + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($t instanceof SkippedTest) { + $this->skipped[] = new TestFailure($test, $t); + $notifyMethod = 'addSkippedTest'; + + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->errors[] = new TestFailure($test, $t); + $notifyMethod = 'addError'; + + if ($this->stopOnError || $this->stopOnFailure) { + $this->stop(); + } + } + + // @see https://github.com/sebastianbergmann/phpunit/issues/1953 + if ($t instanceof Error) { + $t = new ExceptionWrapper($t); + } + + foreach ($this->listeners as $listener) { + $listener->$notifyMethod($test, $t, $time); + } + + $this->lastTestFailed = true; + $this->time += $time; + } + + /** + * Adds a warning to the list of warnings. + * The passed in exception caused the warning. + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + if ($this->stopOnWarning || $this->stopOnDefect) { + $this->stop(); + } + + $this->warnings[] = new TestFailure($test, $e); + + foreach ($this->listeners as $listener) { + $listener->addWarning($test, $e, $time); + } + + $this->time += $time; + } + + /** + * Adds a failure to the list of failures. + * The passed in exception caused the failure. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + if ($e instanceof RiskyTest || $e instanceof OutputError) { + $this->risky[] = new TestFailure($test, $e); + $notifyMethod = 'addRiskyTest'; + + if ($test instanceof TestCase) { + $test->markAsRisky(); + } + + if ($this->stopOnRisky || $this->stopOnDefect) { + $this->stop(); + } + } elseif ($e instanceof IncompleteTest) { + $this->notImplemented[] = new TestFailure($test, $e); + $notifyMethod = 'addIncompleteTest'; + + if ($this->stopOnIncomplete) { + $this->stop(); + } + } elseif ($e instanceof SkippedTest) { + $this->skipped[] = new TestFailure($test, $e); + $notifyMethod = 'addSkippedTest'; + + if ($this->stopOnSkipped) { + $this->stop(); + } + } else { + $this->failures[] = new TestFailure($test, $e); + $notifyMethod = 'addFailure'; + + if ($this->stopOnFailure || $this->stopOnDefect) { + $this->stop(); + } + } + + foreach ($this->listeners as $listener) { + $listener->$notifyMethod($test, $e, $time); + } + + $this->lastTestFailed = true; + $this->time += $time; + } + + /** + * Informs the result that a test suite will be started. + */ + public function startTestSuite(TestSuite $suite): void + { + if ($this->topTestSuite === null) { + $this->topTestSuite = $suite; + } + + foreach ($this->listeners as $listener) { + $listener->startTestSuite($suite); + } + } + + /** + * Informs the result that a test suite was completed. + */ + public function endTestSuite(TestSuite $suite): void + { + foreach ($this->listeners as $listener) { + $listener->endTestSuite($suite); + } + } + + /** + * Informs the result that a test will be started. + */ + public function startTest(Test $test): void + { + $this->lastTestFailed = false; + $this->runTests += \count($test); + + foreach ($this->listeners as $listener) { + $listener->startTest($test); + } + } + + /** + * Informs the result that a test was completed. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function endTest(Test $test, float $time): void + { + foreach ($this->listeners as $listener) { + $listener->endTest($test, $time); + } + + if (!$this->lastTestFailed && $test instanceof TestCase) { + $class = \get_class($test); + $key = $class . '::' . $test->getName(); + + $this->passed[$key] = [ + 'result' => $test->getResult(), + 'size' => \PHPUnit\Util\Test::getSize( + $class, + $test->getName(false) + ), + ]; + + $this->time += $time; + } + } + + /** + * Returns true if no risky test occurred. + */ + public function allHarmless(): bool + { + return $this->riskyCount() == 0; + } + + /** + * Gets the number of risky tests. + */ + public function riskyCount(): int + { + return \count($this->risky); + } + + /** + * Returns true if no incomplete test occurred. + */ + public function allCompletelyImplemented(): bool + { + return $this->notImplementedCount() == 0; + } + + /** + * Gets the number of incomplete tests. + */ + public function notImplementedCount(): int + { + return \count($this->notImplemented); + } + + /** + * Returns an array of TestFailure objects for the risky tests + * + * @return TestFailure[] + */ + public function risky(): array + { + return $this->risky; + } + + /** + * Returns an array of TestFailure objects for the incomplete tests + * + * @return TestFailure[] + */ + public function notImplemented(): array + { + return $this->notImplemented; + } + + /** + * Returns true if no test has been skipped. + */ + public function noneSkipped(): bool + { + return $this->skippedCount() == 0; + } + + /** + * Gets the number of skipped tests. + */ + public function skippedCount(): int + { + return \count($this->skipped); + } + + /** + * Returns an array of TestFailure objects for the skipped tests + * + * @return TestFailure[] + */ + public function skipped(): array + { + return $this->skipped; + } + + /** + * Gets the number of detected errors. + */ + public function errorCount(): int + { + return \count($this->errors); + } + + /** + * Returns an array of TestFailure objects for the errors + * + * @return TestFailure[] + */ + public function errors(): array + { + return $this->errors; + } + + /** + * Gets the number of detected failures. + */ + public function failureCount(): int + { + return \count($this->failures); + } + + /** + * Returns an array of TestFailure objects for the failures + * + * @return TestFailure[] + */ + public function failures(): array + { + return $this->failures; + } + + /** + * Gets the number of detected warnings. + */ + public function warningCount(): int + { + return \count($this->warnings); + } + + /** + * Returns an array of TestFailure objects for the warnings + * + * @return TestFailure[] + */ + public function warnings(): array + { + return $this->warnings; + } + + /** + * Returns the names of the tests that have passed. + */ + public function passed(): array + { + return $this->passed; + } + + /** + * Returns the (top) test suite. + */ + public function topTestSuite(): TestSuite + { + return $this->topTestSuite; + } + + /** + * Returns whether code coverage information should be collected. + */ + public function getCollectCodeCoverageInformation(): bool + { + return $this->codeCoverage !== null; + } + + /** + * Runs a TestCase. + * + * @throws CodeCoverageException + * @throws OriginalCoveredCodeNotExecutedException + * @throws OriginalMissingCoversAnnotationException + * @throws UnintentionallyCoveredCodeException + * @throws \ReflectionException + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws \SebastianBergmann\CodeCoverage\RuntimeException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function run(Test $test): void + { + Assert::resetCount(); + + $coversNothing = false; + + if ($test instanceof TestCase) { + $test->setRegisterMockObjectsFromTestArgumentsRecursively( + $this->registerMockObjectsFromTestArgumentsRecursively + ); + + $isAnyCoverageRequired = self::isAnyCoverageRequired($test); + } + + $error = false; + $failure = false; + $warning = false; + $incomplete = false; + $risky = false; + $skipped = false; + + $this->startTest($test); + + $errorHandlerSet = false; + + if ($this->convertErrorsToExceptions) { + $oldErrorHandler = \set_error_handler( + [ErrorHandler::class, 'handleError'], + \E_ALL | \E_STRICT + ); + + if ($oldErrorHandler === null) { + $errorHandlerSet = true; + } else { + \restore_error_handler(); + } + } + + $collectCodeCoverage = $this->codeCoverage !== null && + !$test instanceof WarningTestCase && + $isAnyCoverageRequired; + + if ($collectCodeCoverage) { + $this->codeCoverage->start($test); + } + + $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && + !$test instanceof WarningTestCase && + $test->getSize() == \PHPUnit\Util\Test::SMALL && + \function_exists('xdebug_start_function_monitor'); + + if ($monitorFunctions) { + /* @noinspection ForgottenDebugOutputInspection */ + \xdebug_start_function_monitor(ResourceOperations::getFunctions()); + } + + Timer::start(); + + try { + if (!$test instanceof WarningTestCase && + $this->enforceTimeLimit && + ($this->defaultTimeLimit || $test->getSize() != \PHPUnit\Util\Test::UNKNOWN) && + \extension_loaded('pcntl') && \class_exists(Invoker::class)) { + switch ($test->getSize()) { + case \PHPUnit\Util\Test::SMALL: + $_timeout = $this->timeoutForSmallTests; + + break; + + case \PHPUnit\Util\Test::MEDIUM: + $_timeout = $this->timeoutForMediumTests; + + break; + + case \PHPUnit\Util\Test::LARGE: + $_timeout = $this->timeoutForLargeTests; + + break; + + case \PHPUnit\Util\Test::UNKNOWN: + $_timeout = $this->defaultTimeLimit; + + break; + } + + $invoker = new Invoker; + $invoker->invoke([$test, 'runBare'], [], $_timeout); + } else { + $test->runBare(); + } + } catch (TimeoutException $e) { + $this->addFailure( + $test, + new RiskyTestError( + $e->getMessage() + ), + $_timeout + ); + + $risky = true; + } catch (MockObjectException $e) { + $e = new Warning( + $e->getMessage() + ); + + $warning = true; + } catch (AssertionFailedError $e) { + $failure = true; + + if ($e instanceof RiskyTestError) { + $risky = true; + } elseif ($e instanceof IncompleteTestError) { + $incomplete = true; + } elseif ($e instanceof SkippedTestError) { + $skipped = true; + } + } catch (AssertionError $e) { + $test->addToAssertionCount(1); + + $failure = true; + $frame = $e->getTrace()[0]; + + $e = new AssertionFailedError( + \sprintf( + '%s in %s:%s', + $e->getMessage(), + $frame['file'], + $frame['line'] + ) + ); + } catch (Warning $e) { + $warning = true; + } catch (Exception $e) { + $error = true; + } catch (Throwable $e) { + $e = new ExceptionWrapper($e); + $error = true; + } + + $time = Timer::stop(); + $test->addToAssertionCount(Assert::getCount()); + + if ($monitorFunctions) { + $blacklist = new Blacklist; + + /** @noinspection ForgottenDebugOutputInspection */ + $functions = \xdebug_get_monitored_functions(); + + /* @noinspection ForgottenDebugOutputInspection */ + \xdebug_stop_function_monitor(); + + foreach ($functions as $function) { + if (!$blacklist->isBlacklisted($function['filename'])) { + $this->addFailure( + $test, + new RiskyTestError( + \sprintf( + '%s() used in %s:%s', + $function['function'], + $function['filename'], + $function['lineno'] + ) + ), + $time + ); + } + } + } + + if ($this->beStrictAboutTestsThatDoNotTestAnything && + $test->getNumAssertions() == 0) { + $risky = true; + } + + if ($collectCodeCoverage) { + $append = !$risky && !$incomplete && !$skipped; + $linesToBeCovered = []; + $linesToBeUsed = []; + + if ($append && $test instanceof TestCase) { + try { + $linesToBeCovered = \PHPUnit\Util\Test::getLinesToBeCovered( + \get_class($test), + $test->getName(false) + ); + + $linesToBeUsed = \PHPUnit\Util\Test::getLinesToBeUsed( + \get_class($test), + $test->getName(false) + ); + } catch (InvalidCoversTargetException $cce) { + $this->addWarning( + $test, + new Warning( + $cce->getMessage() + ), + $time + ); + } + } + + try { + $this->codeCoverage->stop( + $append, + $linesToBeCovered, + $linesToBeUsed + ); + } catch (UnintentionallyCoveredCodeException $cce) { + $this->addFailure( + $test, + new UnintentionallyCoveredCodeError( + 'This test executed code that is not listed as code to be covered or used:' . + \PHP_EOL . $cce->getMessage() + ), + $time + ); + } catch (OriginalCoveredCodeNotExecutedException $cce) { + $this->addFailure( + $test, + new CoveredCodeNotExecutedException( + 'This test did not execute all the code that is listed as code to be covered:' . + \PHP_EOL . $cce->getMessage() + ), + $time + ); + } catch (OriginalMissingCoversAnnotationException $cce) { + if ($linesToBeCovered !== false) { + $this->addFailure( + $test, + new MissingCoversAnnotationException( + 'This test does not have a @covers annotation but is expected to have one' + ), + $time + ); + } + } catch (OriginalCodeCoverageException $cce) { + $error = true; + + $e = $e ?? $cce; + } + } + + if ($errorHandlerSet === true) { + \restore_error_handler(); + } + + if ($error === true) { + $this->addError($test, $e, $time); + } elseif ($failure === true) { + $this->addFailure($test, $e, $time); + } elseif ($warning === true) { + $this->addWarning($test, $e, $time); + } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && + !$test->doesNotPerformAssertions() && + $test->getNumAssertions() == 0) { + $reflected = new \ReflectionClass($test); + $name = $test->getName(false); + + if ($name && $reflected->hasMethod($name)) { + $reflected = $reflected->getMethod($name); + } + $this->addFailure( + $test, + new RiskyTestError(\sprintf( + "This test did not perform any assertions\n\n%s:%d", + $reflected->getFileName(), + $reflected->getStartLine() + )), + $time + ); + } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && + $test->doesNotPerformAssertions() && + $test->getNumAssertions() > 0) { + $this->addFailure( + $test, + new RiskyTestError(\sprintf( + 'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', + $test->getNumAssertions() + )), + $time + ); + } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { + $this->addFailure( + $test, + new OutputError( + \sprintf( + 'This test printed output: %s', + $test->getActualOutput() + ) + ), + $time + ); + } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) { + $annotations = $test->getAnnotations(); + + if (isset($annotations['method']['todo'])) { + $this->addFailure( + $test, + new RiskyTestError( + 'Test method is annotated with @todo' + ), + $time + ); + } + } + + $this->endTest($test, $time); + } + + /** + * Gets the number of run tests. + */ + public function count(): int + { + return $this->runTests; + } + + /** + * Checks whether the test run should stop. + */ + public function shouldStop(): bool + { + return $this->stop; + } + + /** + * Marks that the test run should stop. + */ + public function stop(): void + { + $this->stop = true; + } + + /** + * Returns the code coverage object. + */ + public function getCodeCoverage(): ?CodeCoverage + { + return $this->codeCoverage; + } + + /** + * Sets the code coverage object. + */ + public function setCodeCoverage(CodeCoverage $codeCoverage): void + { + $this->codeCoverage = $codeCoverage; + } + + /** + * Enables or disables the error-to-exception conversion. + */ + public function convertErrorsToExceptions(bool $flag): void + { + $this->convertErrorsToExceptions = $flag; + } + + /** + * Returns the error-to-exception conversion setting. + */ + public function getConvertErrorsToExceptions(): bool + { + return $this->convertErrorsToExceptions; + } + + /** + * Enables or disables the stopping when an error occurs. + */ + public function stopOnError(bool $flag): void + { + $this->stopOnError = $flag; + } + + /** + * Enables or disables the stopping when a failure occurs. + */ + public function stopOnFailure(bool $flag): void + { + $this->stopOnFailure = $flag; + } + + /** + * Enables or disables the stopping when a warning occurs. + */ + public function stopOnWarning(bool $flag): void + { + $this->stopOnWarning = $flag; + } + + public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void + { + $this->beStrictAboutTestsThatDoNotTestAnything = $flag; + } + + public function isStrictAboutTestsThatDoNotTestAnything(): bool + { + return $this->beStrictAboutTestsThatDoNotTestAnything; + } + + public function beStrictAboutOutputDuringTests(bool $flag): void + { + $this->beStrictAboutOutputDuringTests = $flag; + } + + public function isStrictAboutOutputDuringTests(): bool + { + return $this->beStrictAboutOutputDuringTests; + } + + public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void + { + $this->beStrictAboutResourceUsageDuringSmallTests = $flag; + } + + public function isStrictAboutResourceUsageDuringSmallTests(): bool + { + return $this->beStrictAboutResourceUsageDuringSmallTests; + } + + public function enforceTimeLimit(bool $flag): void + { + $this->enforceTimeLimit = $flag; + } + + public function enforcesTimeLimit(): bool + { + return $this->enforceTimeLimit; + } + + public function beStrictAboutTodoAnnotatedTests(bool $flag): void + { + $this->beStrictAboutTodoAnnotatedTests = $flag; + } + + public function isStrictAboutTodoAnnotatedTests(): bool + { + return $this->beStrictAboutTodoAnnotatedTests; + } + + /** + * Enables or disables the stopping for risky tests. + */ + public function stopOnRisky(bool $flag): void + { + $this->stopOnRisky = $flag; + } + + /** + * Enables or disables the stopping for incomplete tests. + */ + public function stopOnIncomplete(bool $flag): void + { + $this->stopOnIncomplete = $flag; + } + + /** + * Enables or disables the stopping for skipped tests. + */ + public function stopOnSkipped(bool $flag): void + { + $this->stopOnSkipped = $flag; + } + + /** + * Enables or disables the stopping for defects: error, failure, warning + */ + public function stopOnDefect(bool $flag): void + { + $this->stopOnDefect = $flag; + } + + /** + * Returns the time spent running the tests. + */ + public function time(): float + { + return $this->time; + } + + /** + * Returns whether the entire test was successful or not. + */ + public function wasSuccessful(): bool + { + return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings); + } + + public function wasSuccessfulIgnoringWarnings(): bool + { + return empty($this->errors) && empty($this->failures); + } + + /** + * Sets the default timeout for tests + */ + public function setDefaultTimeLimit(int $timeout): void + { + $this->defaultTimeLimit = $timeout; + } + + /** + * Sets the timeout for small tests. + */ + public function setTimeoutForSmallTests(int $timeout): void + { + $this->timeoutForSmallTests = $timeout; + } + + /** + * Sets the timeout for medium tests. + */ + public function setTimeoutForMediumTests(int $timeout): void + { + $this->timeoutForMediumTests = $timeout; + } + + /** + * Sets the timeout for large tests. + */ + public function setTimeoutForLargeTests(int $timeout): void + { + $this->timeoutForLargeTests = $timeout; + } + + /** + * Returns the set timeout for large tests. + */ + public function getTimeoutForLargeTests(): int + { + return $this->timeoutForLargeTests; + } + + public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void + { + $this->registerMockObjectsFromTestArgumentsRecursively = $flag; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class CoveredCodeNotExecutedException extends RiskyTestError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class CodeCoverageException extends Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class DataProviderTestSuite extends TestSuite +{ + /** + * @var string[] + */ + private $dependencies = []; + + /** + * @param string[] $dependencies + */ + public function setDependencies(array $dependencies): void + { + $this->dependencies = $dependencies; + + foreach ($this->tests as $test) { + $test->setDependencies($dependencies); + } + } + + public function getDependencies(): array + { + return $this->dependencies; + } + + public function hasDependencies(): bool + { + return \count($this->dependencies) > 0; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * A warning. + */ +class WarningTestCase extends TestCase +{ + /** + * @var string + */ + protected $message = ''; + + /** + * @var bool + */ + protected $backupGlobals = false; + + /** + * @var bool + */ + protected $backupStaticAttributes = false; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * @var bool + */ + protected $useErrorHandler = false; + + /** + * @param string $message + */ + public function __construct($message = '') + { + $this->message = $message; + parent::__construct('Warning'); + } + + public function getMessage(): string + { + return $this->message; + } + + /** + * Returns a string representation of the test case. + */ + public function toString(): string + { + return 'Warning'; + } + + /** + * @throws Exception + */ + protected function runTest(): void + { + throw new Warning($this->message); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\Constraint\ArrayHasKey; +use PHPUnit\Framework\Constraint\Attribute; +use PHPUnit\Framework\Constraint\Callback; +use PHPUnit\Framework\Constraint\ClassHasAttribute; +use PHPUnit\Framework\Constraint\ClassHasStaticAttribute; +use PHPUnit\Framework\Constraint\Constraint; +use PHPUnit\Framework\Constraint\Count; +use PHPUnit\Framework\Constraint\DirectoryExists; +use PHPUnit\Framework\Constraint\FileExists; +use PHPUnit\Framework\Constraint\GreaterThan; +use PHPUnit\Framework\Constraint\IsAnything; +use PHPUnit\Framework\Constraint\IsEmpty; +use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\Constraint\IsFalse; +use PHPUnit\Framework\Constraint\IsFinite; +use PHPUnit\Framework\Constraint\IsIdentical; +use PHPUnit\Framework\Constraint\IsInfinite; +use PHPUnit\Framework\Constraint\IsInstanceOf; +use PHPUnit\Framework\Constraint\IsJson; +use PHPUnit\Framework\Constraint\IsNan; +use PHPUnit\Framework\Constraint\IsNull; +use PHPUnit\Framework\Constraint\IsReadable; +use PHPUnit\Framework\Constraint\IsTrue; +use PHPUnit\Framework\Constraint\IsType; +use PHPUnit\Framework\Constraint\IsWritable; +use PHPUnit\Framework\Constraint\LessThan; +use PHPUnit\Framework\Constraint\LogicalAnd; +use PHPUnit\Framework\Constraint\LogicalNot; +use PHPUnit\Framework\Constraint\LogicalOr; +use PHPUnit\Framework\Constraint\LogicalXor; +use PHPUnit\Framework\Constraint\ObjectHasAttribute; +use PHPUnit\Framework\Constraint\RegularExpression; +use PHPUnit\Framework\Constraint\StringContains; +use PHPUnit\Framework\Constraint\StringEndsWith; +use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; +use PHPUnit\Framework\Constraint\StringStartsWith; +use PHPUnit\Framework\Constraint\TraversableContains; +use PHPUnit\Framework\Constraint\TraversableContainsOnly; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount as AnyInvokedCountMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtIndex as InvokedAtIndexMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastCount as InvokedAtLeastCountMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedAtMostCount as InvokedAtMostCountMatcher; +use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; +use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; +use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; +use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; +use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; +use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; +use PHPUnit\Framework\MockObject\Stub\ReturnStub; +use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; + +/** + * Asserts that an array has a specified key. + * + * @param int|string $key + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertArrayHasKey($key, $array, string $message = ''): void +{ + Assert::assertArrayHasKey(...\func_get_args()); +} + +/** + * Asserts that an array has a specified subset. + * + * @param array|ArrayAccess $subset + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * + * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494 + */ +function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void +{ + Assert::assertArraySubset(...\func_get_args()); +} + +/** + * Asserts that an array does not have a specified key. + * + * @param int|string $key + * @param array|ArrayAccess $array + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertArrayNotHasKey($key, $array, string $message = ''): void +{ + Assert::assertArrayNotHasKey(...\func_get_args()); +} + +/** + * Asserts that a haystack contains a needle. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void +{ + Assert::assertContains(...\func_get_args()); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains a needle. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void +{ + Assert::assertAttributeContains(...\func_get_args()); +} + +/** + * Asserts that a haystack does not contain a needle. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void +{ + Assert::assertNotContains(...\func_get_args()); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain a needle. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void +{ + Assert::assertAttributeNotContains(...\func_get_args()); +} + +/** + * Asserts that a haystack contains only values of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void +{ + Assert::assertContainsOnly(...\func_get_args()); +} + +/** + * Asserts that a haystack contains only instances of a given class name. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void +{ + Assert::assertContainsOnlyInstancesOf(...\func_get_args()); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object contains only values of a given type. + * + * @param object|string $haystackClassOrObject + * @param bool $isNativeType + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void +{ + Assert::assertAttributeContainsOnly(...\func_get_args()); +} + +/** + * Asserts that a haystack does not contain only values of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void +{ + Assert::assertNotContainsOnly(...\func_get_args()); +} + +/** + * Asserts that a haystack that is stored in a static attribute of a class + * or an attribute of an object does not contain only values of a given + * type. + * + * @param object|string $haystackClassOrObject + * @param bool $isNativeType + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void +{ + Assert::assertAttributeNotContainsOnly(...\func_get_args()); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertCount(int $expectedCount, $haystack, string $message = ''): void +{ + Assert::assertCount(...\func_get_args()); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void +{ + Assert::assertAttributeCount(...\func_get_args()); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable. + * + * @param Countable|iterable $haystack + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotCount(int $expectedCount, $haystack, string $message = ''): void +{ + Assert::assertNotCount(...\func_get_args()); +} + +/** + * Asserts the number of elements of an array, Countable or Traversable + * that is stored in an attribute. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void +{ + Assert::assertAttributeNotCount(...\func_get_args()); +} + +/** + * Asserts that two variables are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertEquals(...\func_get_args()); +} + +/** + * Asserts that a variable is equal to an attribute of an object. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertAttributeEquals(...\func_get_args()); +} + +/** + * Asserts that two variables are not equal. + * + * @param float $delta + * @param int $maxDepth + * @param bool $canonicalize + * @param bool $ignoreCase + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void +{ + Assert::assertNotEquals(...\func_get_args()); +} + +/** + * Asserts that a variable is not equal to an attribute of an object. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertAttributeNotEquals(...\func_get_args()); +} + +/** + * Asserts that a variable is empty. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertEmpty($actual, string $message = ''): void +{ + Assert::assertEmpty(...\func_get_args()); +} + +/** + * Asserts that a static attribute of a class or an attribute of an object + * is empty. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void +{ + Assert::assertAttributeEmpty(...\func_get_args()); +} + +/** + * Asserts that a variable is not empty. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotEmpty($actual, string $message = ''): void +{ + Assert::assertNotEmpty(...\func_get_args()); +} + +/** + * Asserts that a static attribute of a class or an attribute of an object + * is not empty. + * + * @param object|string $haystackClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void +{ + Assert::assertAttributeNotEmpty(...\func_get_args()); +} + +/** + * Asserts that a value is greater than another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertGreaterThan($expected, $actual, string $message = ''): void +{ + Assert::assertGreaterThan(...\func_get_args()); +} + +/** + * Asserts that an attribute is greater than another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void +{ + Assert::assertAttributeGreaterThan(...\func_get_args()); +} + +/** + * Asserts that a value is greater than or equal to another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void +{ + Assert::assertGreaterThanOrEqual(...\func_get_args()); +} + +/** + * Asserts that an attribute is greater than or equal to another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void +{ + Assert::assertAttributeGreaterThanOrEqual(...\func_get_args()); +} + +/** + * Asserts that a value is smaller than another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertLessThan($expected, $actual, string $message = ''): void +{ + Assert::assertLessThan(...\func_get_args()); +} + +/** + * Asserts that an attribute is smaller than another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void +{ + Assert::assertAttributeLessThan(...\func_get_args()); +} + +/** + * Asserts that a value is smaller than or equal to another value. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertLessThanOrEqual($expected, $actual, string $message = ''): void +{ + Assert::assertLessThanOrEqual(...\func_get_args()); +} + +/** + * Asserts that an attribute is smaller than or equal to another value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void +{ + Assert::assertAttributeLessThanOrEqual(...\func_get_args()); +} + +/** + * Asserts that the contents of one file is equal to the contents of another + * file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertFileEquals(...\func_get_args()); +} + +/** + * Asserts that the contents of one file is not equal to the contents of + * another file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertFileNotEquals(...\func_get_args()); +} + +/** + * Asserts that the contents of a string is equal + * to the contents of a file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertStringEqualsFile(...\func_get_args()); +} + +/** + * Asserts that the contents of a string is not equal + * to the contents of a file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void +{ + Assert::assertStringNotEqualsFile(...\func_get_args()); +} + +/** + * Asserts that a file/dir is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertIsReadable(string $filename, string $message = ''): void +{ + Assert::assertIsReadable(...\func_get_args()); +} + +/** + * Asserts that a file/dir exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotIsReadable(string $filename, string $message = ''): void +{ + Assert::assertNotIsReadable(...\func_get_args()); +} + +/** + * Asserts that a file/dir exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertIsWritable(string $filename, string $message = ''): void +{ + Assert::assertIsWritable(...\func_get_args()); +} + +/** + * Asserts that a file/dir exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotIsWritable(string $filename, string $message = ''): void +{ + Assert::assertNotIsWritable(...\func_get_args()); +} + +/** + * Asserts that a directory exists. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertDirectoryExists(string $directory, string $message = ''): void +{ + Assert::assertDirectoryExists(...\func_get_args()); +} + +/** + * Asserts that a directory does not exist. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertDirectoryNotExists(string $directory, string $message = ''): void +{ + Assert::assertDirectoryNotExists(...\func_get_args()); +} + +/** + * Asserts that a directory exists and is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertDirectoryIsReadable(string $directory, string $message = ''): void +{ + Assert::assertDirectoryIsReadable(...\func_get_args()); +} + +/** + * Asserts that a directory exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertDirectoryNotIsReadable(string $directory, string $message = ''): void +{ + Assert::assertDirectoryNotIsReadable(...\func_get_args()); +} + +/** + * Asserts that a directory exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertDirectoryIsWritable(string $directory, string $message = ''): void +{ + Assert::assertDirectoryIsWritable(...\func_get_args()); +} + +/** + * Asserts that a directory exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertDirectoryNotIsWritable(string $directory, string $message = ''): void +{ + Assert::assertDirectoryNotIsWritable(...\func_get_args()); +} + +/** + * Asserts that a file exists. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileExists(string $filename, string $message = ''): void +{ + Assert::assertFileExists(...\func_get_args()); +} + +/** + * Asserts that a file does not exist. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileNotExists(string $filename, string $message = ''): void +{ + Assert::assertFileNotExists(...\func_get_args()); +} + +/** + * Asserts that a file exists and is readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileIsReadable(string $file, string $message = ''): void +{ + Assert::assertFileIsReadable(...\func_get_args()); +} + +/** + * Asserts that a file exists and is not readable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileNotIsReadable(string $file, string $message = ''): void +{ + Assert::assertFileNotIsReadable(...\func_get_args()); +} + +/** + * Asserts that a file exists and is writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileIsWritable(string $file, string $message = ''): void +{ + Assert::assertFileIsWritable(...\func_get_args()); +} + +/** + * Asserts that a file exists and is not writable. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFileNotIsWritable(string $file, string $message = ''): void +{ + Assert::assertFileNotIsWritable(...\func_get_args()); +} + +/** + * Asserts that a condition is true. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertTrue($condition, string $message = ''): void +{ + Assert::assertTrue(...\func_get_args()); +} + +/** + * Asserts that a condition is not true. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotTrue($condition, string $message = ''): void +{ + Assert::assertNotTrue(...\func_get_args()); +} + +/** + * Asserts that a condition is false. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFalse($condition, string $message = ''): void +{ + Assert::assertFalse(...\func_get_args()); +} + +/** + * Asserts that a condition is not false. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotFalse($condition, string $message = ''): void +{ + Assert::assertNotFalse(...\func_get_args()); +} + +/** + * Asserts that a variable is null. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNull($actual, string $message = ''): void +{ + Assert::assertNull(...\func_get_args()); +} + +/** + * Asserts that a variable is not null. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotNull($actual, string $message = ''): void +{ + Assert::assertNotNull(...\func_get_args()); +} + +/** + * Asserts that a variable is finite. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertFinite($actual, string $message = ''): void +{ + Assert::assertFinite(...\func_get_args()); +} + +/** + * Asserts that a variable is infinite. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertInfinite($actual, string $message = ''): void +{ + Assert::assertInfinite(...\func_get_args()); +} + +/** + * Asserts that a variable is nan. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNan($actual, string $message = ''): void +{ + Assert::assertNan(...\func_get_args()); +} + +/** + * Asserts that a class has a specified attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void +{ + Assert::assertClassHasAttribute(...\func_get_args()); +} + +/** + * Asserts that a class does not have a specified attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void +{ + Assert::assertClassNotHasAttribute(...\func_get_args()); +} + +/** + * Asserts that a class has a specified static attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void +{ + Assert::assertClassHasStaticAttribute(...\func_get_args()); +} + +/** + * Asserts that a class does not have a specified static attribute. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void +{ + Assert::assertClassNotHasStaticAttribute(...\func_get_args()); +} + +/** + * Asserts that an object has a specified attribute. + * + * @param object $object + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void +{ + Assert::assertObjectHasAttribute(...\func_get_args()); +} + +/** + * Asserts that an object does not have a specified attribute. + * + * @param object $object + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void +{ + Assert::assertObjectNotHasAttribute(...\func_get_args()); +} + +/** + * Asserts that two variables have the same type and value. + * Used on objects, it asserts that two variables reference + * the same object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertSame($expected, $actual, string $message = ''): void +{ + Assert::assertSame(...\func_get_args()); +} + +/** + * Asserts that a variable and an attribute of an object have the same type + * and value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void +{ + Assert::assertAttributeSame(...\func_get_args()); +} + +/** + * Asserts that two variables do not have the same type and value. + * Used on objects, it asserts that two variables do not reference + * the same object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotSame($expected, $actual, string $message = ''): void +{ + Assert::assertNotSame(...\func_get_args()); +} + +/** + * Asserts that a variable and an attribute of an object do not have the + * same type and value. + * + * @param object|string $actualClassOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void +{ + Assert::assertAttributeNotSame(...\func_get_args()); +} + +/** + * Asserts that a variable is of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertInstanceOf(string $expected, $actual, string $message = ''): void +{ + Assert::assertInstanceOf(...\func_get_args()); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void +{ + Assert::assertAttributeInstanceOf(...\func_get_args()); +} + +/** + * Asserts that a variable is not of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotInstanceOf(string $expected, $actual, string $message = ''): void +{ + Assert::assertNotInstanceOf(...\func_get_args()); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void +{ + Assert::assertAttributeNotInstanceOf(...\func_get_args()); +} + +/** + * Asserts that a variable is of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertInternalType(string $expected, $actual, string $message = ''): void +{ + Assert::assertInternalType(...\func_get_args()); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void +{ + Assert::assertAttributeInternalType(...\func_get_args()); +} + +/** + * Asserts that a variable is not of a given type. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotInternalType(string $expected, $actual, string $message = ''): void +{ + Assert::assertNotInternalType(...\func_get_args()); +} + +/** + * Asserts that an attribute is of a given type. + * + * @param object|string $classOrObject + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void +{ + Assert::assertAttributeNotInternalType(...\func_get_args()); +} + +/** + * Asserts that a string matches a given regular expression. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertRegExp(string $pattern, string $string, string $message = ''): void +{ + Assert::assertRegExp(...\func_get_args()); +} + +/** + * Asserts that a string does not match a given regular expression. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotRegExp(string $pattern, string $string, string $message = ''): void +{ + Assert::assertNotRegExp(...\func_get_args()); +} + +/** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is the same. + * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertSameSize($expected, $actual, string $message = ''): void +{ + Assert::assertSameSize(...\func_get_args()); +} + +/** + * Assert that the size of two arrays (or `Countable` or `Traversable` objects) + * is not the same. + * + * @param Countable|iterable $expected + * @param Countable|iterable $actual + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertNotSameSize($expected, $actual, string $message = ''): void +{ + Assert::assertNotSameSize(...\func_get_args()); +} + +/** + * Asserts that a string matches a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringMatchesFormat(string $format, string $string, string $message = ''): void +{ + Assert::assertStringMatchesFormat(...\func_get_args()); +} + +/** + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void +{ + Assert::assertStringNotMatchesFormat(...\func_get_args()); +} + +/** + * Asserts that a string matches a given format file. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void +{ + Assert::assertStringMatchesFormatFile(...\func_get_args()); +} + +/** + * Asserts that a string does not match a given format string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void +{ + Assert::assertStringNotMatchesFormatFile(...\func_get_args()); +} + +/** + * Asserts that a string starts with a given prefix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringStartsWith(string $prefix, string $string, string $message = ''): void +{ + Assert::assertStringStartsWith(...\func_get_args()); +} + +/** + * Asserts that a string starts not with a given prefix. + * + * @param string $prefix + * @param string $string + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringStartsNotWith($prefix, $string, string $message = ''): void +{ + Assert::assertStringStartsNotWith(...\func_get_args()); +} + +/** + * Asserts that a string ends with a given suffix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringEndsWith(string $suffix, string $string, string $message = ''): void +{ + Assert::assertStringEndsWith(...\func_get_args()); +} + +/** + * Asserts that a string ends not with a given suffix. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void +{ + Assert::assertStringEndsNotWith(...\func_get_args()); +} + +/** + * Asserts that two XML files are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void +{ + Assert::assertXmlFileEqualsXmlFile(...\func_get_args()); +} + +/** + * Asserts that two XML files are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void +{ + Assert::assertXmlFileNotEqualsXmlFile(...\func_get_args()); +} + +/** + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void +{ + Assert::assertXmlStringEqualsXmlFile(...\func_get_args()); +} + +/** + * Asserts that two XML documents are not equal. + * + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void +{ + Assert::assertXmlStringNotEqualsXmlFile(...\func_get_args()); +} + +/** + * Asserts that two XML documents are equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void +{ + Assert::assertXmlStringEqualsXmlString(...\func_get_args()); +} + +/** + * Asserts that two XML documents are not equal. + * + * @param DOMDocument|string $expectedXml + * @param DOMDocument|string $actualXml + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void +{ + Assert::assertXmlStringNotEqualsXmlString(...\func_get_args()); +} + +/** + * Asserts that a hierarchy of DOMElements matches. + * + * @throws AssertionFailedError + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void +{ + Assert::assertEqualXMLStructure(...\func_get_args()); +} + +/** + * Evaluates a PHPUnit\Framework\Constraint matcher object. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertThat($value, Constraint $constraint, string $message = ''): void +{ + Assert::assertThat(...\func_get_args()); +} + +/** + * Asserts that a string is a valid JSON string. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJson(string $actualJson, string $message = ''): void +{ + Assert::assertJson(...\func_get_args()); +} + +/** + * Asserts that two given JSON encoded objects or arrays are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void +{ + Assert::assertJsonStringEqualsJsonString(...\func_get_args()); +} + +/** + * Asserts that two given JSON encoded objects or arrays are not equal. + * + * @param string $expectedJson + * @param string $actualJson + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void +{ + Assert::assertJsonStringNotEqualsJsonString(...\func_get_args()); +} + +/** + * Asserts that the generated JSON encoded object and the content of the given file are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void +{ + Assert::assertJsonStringEqualsJsonFile(...\func_get_args()); +} + +/** + * Asserts that the generated JSON encoded object and the content of the given file are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void +{ + Assert::assertJsonStringNotEqualsJsonFile(...\func_get_args()); +} + +/** + * Asserts that two JSON files are equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void +{ + Assert::assertJsonFileEqualsJsonFile(...\func_get_args()); +} + +/** + * Asserts that two JSON files are not equal. + * + * @throws ExpectationFailedException + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ +function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void +{ + Assert::assertJsonFileNotEqualsJsonFile(...\func_get_args()); +} + +function logicalAnd(): LogicalAnd +{ + return Assert::logicalAnd(...\func_get_args()); +} + +function logicalOr(): LogicalOr +{ + return Assert::logicalOr(...\func_get_args()); +} + +function logicalNot(Constraint $constraint): LogicalNot +{ + return Assert::logicalNot(...\func_get_args()); +} + +function logicalXor(): LogicalXor +{ + return Assert::logicalXor(...\func_get_args()); +} + +function anything(): IsAnything +{ + return Assert::anything(); +} + +function isTrue(): IsTrue +{ + return Assert::isTrue(); +} + +function callback(callable $callback): Callback +{ + return Assert::callback(...\func_get_args()); +} + +function isFalse(): IsFalse +{ + return Assert::isFalse(); +} + +function isJson(): IsJson +{ + return Assert::isJson(); +} + +function isNull(): IsNull +{ + return Assert::isNull(); +} + +function isFinite(): IsFinite +{ + return Assert::isFinite(); +} + +function isInfinite(): IsInfinite +{ + return Assert::isInfinite(); +} + +function isNan(): IsNan +{ + return Assert::isNan(); +} + +function attribute(Constraint $constraint, string $attributeName): Attribute +{ + return Assert::attribute(...\func_get_args()); +} + +function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains +{ + return Assert::contains(...\func_get_args()); +} + +function containsOnly(string $type): TraversableContainsOnly +{ + return Assert::containsOnly(...\func_get_args()); +} + +function containsOnlyInstancesOf(string $className): TraversableContainsOnly +{ + return Assert::containsOnlyInstancesOf(...\func_get_args()); +} + +function arrayHasKey($key): ArrayHasKey +{ + return Assert::arrayHasKey(...\func_get_args()); +} + +function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual +{ + return Assert::equalTo(...\func_get_args()); +} + +function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute +{ + return Assert::attributeEqualTo(...\func_get_args()); +} + +function isEmpty(): IsEmpty +{ + return Assert::isEmpty(); +} + +function isWritable(): IsWritable +{ + return Assert::isWritable(); +} + +function isReadable(): IsReadable +{ + return Assert::isReadable(); +} + +function directoryExists(): DirectoryExists +{ + return Assert::directoryExists(); +} + +function fileExists(): FileExists +{ + return Assert::fileExists(); +} + +function greaterThan($value): GreaterThan +{ + return Assert::greaterThan(...\func_get_args()); +} + +function greaterThanOrEqual($value): LogicalOr +{ + return Assert::greaterThanOrEqual(...\func_get_args()); +} + +function classHasAttribute(string $attributeName): ClassHasAttribute +{ + return Assert::classHasAttribute(...\func_get_args()); +} + +function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute +{ + return Assert::classHasStaticAttribute(...\func_get_args()); +} + +function objectHasAttribute($attributeName): ObjectHasAttribute +{ + return Assert::objectHasAttribute(...\func_get_args()); +} + +function identicalTo($value): IsIdentical +{ + return Assert::identicalTo(...\func_get_args()); +} + +function isInstanceOf(string $className): IsInstanceOf +{ + return Assert::isInstanceOf(...\func_get_args()); +} + +function isType(string $type): IsType +{ + return Assert::isType(...\func_get_args()); +} + +function lessThan($value): LessThan +{ + return Assert::lessThan(...\func_get_args()); +} + +function lessThanOrEqual($value): LogicalOr +{ + return Assert::lessThanOrEqual(...\func_get_args()); +} + +function matchesRegularExpression(string $pattern): RegularExpression +{ + return Assert::matchesRegularExpression(...\func_get_args()); +} + +function matches(string $string): StringMatchesFormatDescription +{ + return Assert::matches(...\func_get_args()); +} + +function stringStartsWith($prefix): StringStartsWith +{ + return Assert::stringStartsWith(...\func_get_args()); +} + +function stringContains(string $string, bool $case = true): StringContains +{ + return Assert::stringContains(...\func_get_args()); +} + +function stringEndsWith(string $suffix): StringEndsWith +{ + return Assert::stringEndsWith(...\func_get_args()); +} + +function countOf(int $count): Count +{ + return Assert::countOf(...\func_get_args()); +} + +/** + * Returns a matcher that matches when the method is executed + * zero or more times. + */ +function any(): AnyInvokedCountMatcher +{ + return new AnyInvokedCountMatcher; +} + +/** + * Returns a matcher that matches when the method is never executed. + */ +function never(): InvokedCountMatcher +{ + return new InvokedCountMatcher(0); +} + +/** + * Returns a matcher that matches when the method is executed + * at least N times. + * + * @param int $requiredInvocations + */ +function atLeast($requiredInvocations): InvokedAtLeastCountMatcher +{ + return new InvokedAtLeastCountMatcher( + $requiredInvocations + ); +} + +/** + * Returns a matcher that matches when the method is executed at least once. + */ +function atLeastOnce(): InvokedAtLeastOnceMatcher +{ + return new InvokedAtLeastOnceMatcher; +} + +/** + * Returns a matcher that matches when the method is executed exactly once. + */ +function once(): InvokedCountMatcher +{ + return new InvokedCountMatcher(1); +} + +/** + * Returns a matcher that matches when the method is executed + * exactly $count times. + * + * @param int $count + */ +function exactly($count): InvokedCountMatcher +{ + return new InvokedCountMatcher($count); +} + +/** + * Returns a matcher that matches when the method is executed + * at most N times. + * + * @param int $allowedInvocations + */ +function atMost($allowedInvocations): InvokedAtMostCountMatcher +{ + return new InvokedAtMostCountMatcher($allowedInvocations); +} + +/** + * Returns a matcher that matches when the method is executed + * at the given index. + * + * @param int $index + */ +function at($index): InvokedAtIndexMatcher +{ + return new InvokedAtIndexMatcher($index); +} + +function returnValue($value): ReturnStub +{ + return new ReturnStub($value); +} + +function returnValueMap(array $valueMap): ReturnValueMapStub +{ + return new ReturnValueMapStub($valueMap); +} + +/** + * @param int $argumentIndex + */ +function returnArgument($argumentIndex): ReturnArgumentStub +{ + return new ReturnArgumentStub($argumentIndex); +} + +function returnCallback($callback): ReturnCallbackStub +{ + return new ReturnCallbackStub($callback); +} + +/** + * Returns the current object. + * + * This method is useful when mocking a fluent interface. + */ +function returnSelf(): ReturnSelfStub +{ + return new ReturnSelfStub; +} + +function throwException(Throwable $exception): ExceptionStub +{ + return new ExceptionStub($exception); +} + +function onConsecutiveCalls(): ConsecutiveCallsStub +{ + $args = \func_get_args(); + + return new ConsecutiveCallsStub($args); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use Iterator; +use IteratorAggregate; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Runner\Filter\Factory; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\FileLoader; +use PHPUnit\Util\InvalidArgumentHelper; +use ReflectionClass; +use ReflectionMethod; +use Throwable; + +/** + * A TestSuite is a composite of Tests. It runs a collection of test cases. + */ +class TestSuite implements Test, SelfDescribing, IteratorAggregate +{ + /** + * Enable or disable the backup and restoration of the $GLOBALS array. + * + * @var bool + */ + protected $backupGlobals; + + /** + * Enable or disable the backup and restoration of static attributes. + * + * @var bool + */ + protected $backupStaticAttributes; + + /** + * @var bool + */ + protected $runTestInSeparateProcess = false; + + /** + * The name of the test suite. + * + * @var string + */ + protected $name = ''; + + /** + * The test groups of the test suite. + * + * @var array + */ + protected $groups = []; + + /** + * The tests in the test suite. + * + * @var TestCase[] + */ + protected $tests = []; + + /** + * The number of tests in the test suite. + * + * @var int + */ + protected $numTests = -1; + + /** + * @var bool + */ + protected $testCase = false; + + /** + * @var array + */ + protected $foundClasses = []; + + /** + * Last count of tests in this suite. + * + * @var null|int + */ + private $cachedNumTests; + + /** + * @var bool + */ + private $beStrictAboutChangesToGlobalState; + + /** + * @var Factory + */ + private $iteratorFilter; + + /** + * @var string[] + */ + private $declaredClasses; + + /** + * @param string $name + * + * @throws Exception + */ + public static function createTest(ReflectionClass $theClass, $name): Test + { + $className = $theClass->getName(); + + if (!$theClass->isInstantiable()) { + return self::warning( + \sprintf('Cannot instantiate class "%s".', $className) + ); + } + + $backupSettings = \PHPUnit\Util\Test::getBackupSettings( + $className, + $name + ); + + $preserveGlobalState = \PHPUnit\Util\Test::getPreserveGlobalStateSettings( + $className, + $name + ); + + $runTestInSeparateProcess = \PHPUnit\Util\Test::getProcessIsolationSettings( + $className, + $name + ); + + $runClassInSeparateProcess = \PHPUnit\Util\Test::getClassProcessIsolationSettings( + $className, + $name + ); + + $constructor = $theClass->getConstructor(); + + if ($constructor === null) { + throw new Exception('No valid test provided.'); + } + $parameters = $constructor->getParameters(); + + // TestCase() or TestCase($name) + if (\count($parameters) < 2) { + $test = new $className; + } // TestCase($name, $data) + else { + try { + $data = \PHPUnit\Util\Test::getProvidedData( + $className, + $name + ); + } catch (IncompleteTestError $e) { + $message = \sprintf( + 'Test for %s::%s marked incomplete by data provider', + $className, + $name + ); + + $_message = $e->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::incompleteTest($className, $name, $message); + } catch (SkippedTestError $e) { + $message = \sprintf( + 'Test for %s::%s skipped by data provider', + $className, + $name + ); + + $_message = $e->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::skipTest($className, $name, $message); + } catch (Throwable $t) { + $message = \sprintf( + 'The data provider specified for %s::%s is invalid.', + $className, + $name + ); + + $_message = $t->getMessage(); + + if (!empty($_message)) { + $message .= "\n" . $_message; + } + + $data = self::warning($message); + } + + // Test method with @dataProvider. + if (isset($data)) { + $test = new DataProviderTestSuite( + $className . '::' . $name + ); + + if (empty($data)) { + $data = self::warning( + \sprintf( + 'No tests found in suite "%s".', + $test->getName() + ) + ); + } + + $groups = \PHPUnit\Util\Test::getGroups($className, $name); + + if ($data instanceof WarningTestCase || + $data instanceof SkippedTestCase || + $data instanceof IncompleteTestCase) { + $test->addTest($data, $groups); + } else { + foreach ($data as $_dataName => $_data) { + $_test = new $className($name, $_data, $_dataName); + + /* @var TestCase $_test */ + + if ($runTestInSeparateProcess) { + $_test->setRunTestInSeparateProcess(true); + + if ($preserveGlobalState !== null) { + $_test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($runClassInSeparateProcess) { + $_test->setRunClassInSeparateProcess(true); + + if ($preserveGlobalState !== null) { + $_test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($backupSettings['backupGlobals'] !== null) { + $_test->setBackupGlobals( + $backupSettings['backupGlobals'] + ); + } + + if ($backupSettings['backupStaticAttributes'] !== null) { + $_test->setBackupStaticAttributes( + $backupSettings['backupStaticAttributes'] + ); + } + + $test->addTest($_test, $groups); + } + } + } else { + $test = new $className; + } + } + + if ($test instanceof TestCase) { + $test->setName($name); + + if ($runTestInSeparateProcess) { + $test->setRunTestInSeparateProcess(true); + + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($runClassInSeparateProcess) { + $test->setRunClassInSeparateProcess(true); + + if ($preserveGlobalState !== null) { + $test->setPreserveGlobalState($preserveGlobalState); + } + } + + if ($backupSettings['backupGlobals'] !== null) { + $test->setBackupGlobals($backupSettings['backupGlobals']); + } + + if ($backupSettings['backupStaticAttributes'] !== null) { + $test->setBackupStaticAttributes( + $backupSettings['backupStaticAttributes'] + ); + } + } + + return $test; + } + + public static function isTestMethod(ReflectionMethod $method): bool + { + if (\strpos($method->name, 'test') === 0) { + return true; + } + + $annotations = \PHPUnit\Util\Test::parseAnnotations($method->getDocComment()); + + return isset($annotations['test']); + } + + /** + * Constructs a new TestSuite: + * + * - PHPUnit\Framework\TestSuite() constructs an empty TestSuite. + * + * - PHPUnit\Framework\TestSuite(ReflectionClass) constructs a + * TestSuite from the given class. + * + * - PHPUnit\Framework\TestSuite(ReflectionClass, String) + * constructs a TestSuite from the given class with the given + * name. + * + * - PHPUnit\Framework\TestSuite(String) either constructs a + * TestSuite from the given class (if the passed string is the + * name of an existing class) or constructs an empty TestSuite + * with the given name. + * + * @param string $name + * + * @throws Exception + */ + public function __construct($theClass = '', $name = '') + { + $this->declaredClasses = \get_declared_classes(); + + $argumentsValid = false; + + if (\is_object($theClass) && + $theClass instanceof ReflectionClass) { + $argumentsValid = true; + } elseif (\is_string($theClass) && + $theClass !== '' && + \class_exists($theClass, true)) { + $argumentsValid = true; + + if ($name == '') { + $name = $theClass; + } + + $theClass = new ReflectionClass($theClass); + } elseif (\is_string($theClass)) { + $this->setName($theClass); + + return; + } + + if (!$argumentsValid) { + throw new Exception; + } + + if (!$theClass->isSubclassOf(TestCase::class)) { + $this->setName($theClass); + + return; + } + + if ($name != '') { + $this->setName($name); + } else { + $this->setName($theClass->getName()); + } + + $constructor = $theClass->getConstructor(); + + if ($constructor !== null && + !$constructor->isPublic()) { + $this->addTest( + self::warning( + \sprintf( + 'Class "%s" has no public constructor.', + $theClass->getName() + ) + ) + ); + + return; + } + + foreach ($theClass->getMethods() as $method) { + if ($method->getDeclaringClass()->getName() === Assert::class) { + continue; + } + + if ($method->getDeclaringClass()->getName() === TestCase::class) { + continue; + } + + $this->addTestMethod($theClass, $method); + } + + if (empty($this->tests)) { + $this->addTest( + self::warning( + \sprintf( + 'No tests found in class "%s".', + $theClass->getName() + ) + ) + ); + } + + $this->testCase = true; + } + + /** + * Template Method that is called before the tests + * of this test suite are run. + */ + protected function setUp(): void + { + } + + /** + * Template Method that is called after the tests + * of this test suite have finished running. + */ + protected function tearDown(): void + { + } + + /** + * Returns a string representation of the test suite. + */ + public function toString(): string + { + return $this->getName(); + } + + /** + * Adds a test to the suite. + * + * @param array $groups + */ + public function addTest(Test $test, $groups = []): void + { + $class = new ReflectionClass($test); + + if (!$class->isAbstract()) { + $this->tests[] = $test; + $this->numTests = -1; + + if ($test instanceof self && empty($groups)) { + $groups = $test->getGroups(); + } + + if (empty($groups)) { + $groups = ['default']; + } + + foreach ($groups as $group) { + if (!isset($this->groups[$group])) { + $this->groups[$group] = [$test]; + } else { + $this->groups[$group][] = $test; + } + } + + if ($test instanceof TestCase) { + $test->setGroups($groups); + } + } + } + + /** + * Adds the tests from the given class to the suite. + * + * @throws Exception + */ + public function addTestSuite($testClass): void + { + if (\is_string($testClass) && \class_exists($testClass)) { + $testClass = new ReflectionClass($testClass); + } + + if (!\is_object($testClass)) { + throw InvalidArgumentHelper::factory( + 1, + 'class name or object' + ); + } + + if ($testClass instanceof self) { + $this->addTest($testClass); + } elseif ($testClass instanceof ReflectionClass) { + $suiteMethod = false; + + if (!$testClass->isAbstract() && $testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { + $method = $testClass->getMethod( + BaseTestRunner::SUITE_METHODNAME + ); + + if ($method->isStatic()) { + $this->addTest( + $method->invoke(null, $testClass->getName()) + ); + + $suiteMethod = true; + } + } + + if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(TestCase::class)) { + $this->addTest(new self($testClass)); + } + } else { + throw new Exception; + } + } + + /** + * Wraps both addTest() and addTestSuite + * as well as the separate import statements for the user's convenience. + * + * If the named file cannot be read or there are no new tests that can be + * added, a PHPUnit\Framework\WarningTestCase will be created instead, + * leaving the current test run untouched. + * + * @throws Exception + */ + public function addTestFile(string $filename): void + { + if (\file_exists($filename) && \substr($filename, -5) == '.phpt') { + $this->addTest( + new PhptTestCase($filename) + ); + + return; + } + + // The given file may contain further stub classes in addition to the + // test class itself. Figure out the actual test class. + $filename = FileLoader::checkAndLoad($filename); + $newClasses = \array_diff(\get_declared_classes(), $this->declaredClasses); + + // The diff is empty in case a parent class (with test methods) is added + // AFTER a child class that inherited from it. To account for that case, + // accumulate all discovered classes, so the parent class may be found in + // a later invocation. + if (!empty($newClasses)) { + // On the assumption that test classes are defined first in files, + // process discovered classes in approximate LIFO order, so as to + // avoid unnecessary reflection. + $this->foundClasses = \array_merge($newClasses, $this->foundClasses); + $this->declaredClasses = \get_declared_classes(); + } + + // The test class's name must match the filename, either in full, or as + // a PEAR/PSR-0 prefixed short name ('NameSpace_ShortName'), or as a + // PSR-1 local short name ('NameSpace\ShortName'). The comparison must be + // anchored to prevent false-positive matches (e.g., 'OtherShortName'). + $shortName = \basename($filename, '.php'); + $shortNameRegEx = '/(?:^|_|\\\\)' . \preg_quote($shortName, '/') . '$/'; + + foreach ($this->foundClasses as $i => $className) { + if (\preg_match($shortNameRegEx, $className)) { + $class = new ReflectionClass($className); + + if ($class->getFileName() == $filename) { + $newClasses = [$className]; + unset($this->foundClasses[$i]); + + break; + } + } + } + + foreach ($newClasses as $className) { + $class = new ReflectionClass($className); + + if (\dirname($class->getFileName()) === __DIR__) { + continue; + } + + if (!$class->isAbstract()) { + if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { + $method = $class->getMethod( + BaseTestRunner::SUITE_METHODNAME + ); + + if ($method->isStatic()) { + $this->addTest($method->invoke(null, $className)); + } + } elseif ($class->implementsInterface(Test::class)) { + $this->addTestSuite($class); + } + } + } + + $this->numTests = -1; + } + + /** + * Wrapper for addTestFile() that adds multiple test files. + * + * @param array|Iterator $fileNames + * + * @throws Exception + */ + public function addTestFiles($fileNames): void + { + if (!(\is_array($fileNames) || + (\is_object($fileNames) && $fileNames instanceof Iterator))) { + throw InvalidArgumentHelper::factory( + 1, + 'array or iterator' + ); + } + + foreach ($fileNames as $filename) { + $this->addTestFile((string) $filename); + } + } + + /** + * Counts the number of test cases that will be run by this test. + * + * @param bool $preferCache indicates if cache is preferred + */ + public function count($preferCache = false): int + { + if ($preferCache && $this->cachedNumTests !== null) { + return $this->cachedNumTests; + } + + $numTests = 0; + + foreach ($this as $test) { + $numTests += \count($test); + } + + $this->cachedNumTests = $numTests; + + return $numTests; + } + + /** + * Returns the name of the suite. + */ + public function getName(): string + { + return $this->name; + } + + /** + * Returns the test groups of the suite. + */ + public function getGroups(): array + { + return \array_keys($this->groups); + } + + public function getGroupDetails() + { + return $this->groups; + } + + /** + * Set tests groups of the test case + */ + public function setGroupDetails(array $groups): void + { + $this->groups = $groups; + } + + /** + * Runs the tests and collects their result in a TestResult. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function run(TestResult $result = null): TestResult + { + if ($result === null) { + $result = $this->createResult(); + } + + if (\count($this) == 0) { + return $result; + } + + $hookMethods = \PHPUnit\Util\Test::getHookMethods($this->name); + + $result->startTestSuite($this); + + try { + $this->setUp(); + + foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { + if ($this->testCase === true && + \class_exists($this->name, false) && + \method_exists($this->name, $beforeClassMethod)) { + if ($missingRequirements = \PHPUnit\Util\Test::getMissingRequirements($this->name, $beforeClassMethod)) { + $this->markTestSuiteSkipped(\implode(\PHP_EOL, $missingRequirements)); + } + + \call_user_func([$this->name, $beforeClassMethod]); + } + } + } catch (SkippedTestSuiteError $error) { + foreach ($this->tests() as $test) { + $result->startTest($test); + $result->addFailure($test, $error, 0); + $result->endTest($test, 0); + } + + $this->tearDown(); + $result->endTestSuite($this); + + return $result; + } catch (Throwable $t) { + foreach ($this->tests() as $test) { + if ($result->shouldStop()) { + break; + } + + $result->startTest($test); + $result->addError($test, $t, 0); + $result->endTest($test, 0); + } + + $this->tearDown(); + $result->endTestSuite($this); + + return $result; + } + + foreach ($this as $test) { + if ($result->shouldStop()) { + break; + } + + if ($test instanceof TestCase || $test instanceof self) { + $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); + $test->setBackupGlobals($this->backupGlobals); + $test->setBackupStaticAttributes($this->backupStaticAttributes); + $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); + } + + $test->run($result); + } + + try { + foreach ($hookMethods['afterClass'] as $afterClassMethod) { + if ($this->testCase === true && \class_exists($this->name, false) && \method_exists( + $this->name, + $afterClassMethod + )) { + \call_user_func([$this->name, $afterClassMethod]); + } + } + } catch (Throwable $t) { + $message = "Exception in {$this->name}::$afterClassMethod" . \PHP_EOL . $t->getMessage(); + $error = new SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace()); + $test = new \Failure($afterClassMethod); + + $result->startTest($test); + $result->addFailure($test, $error, 0); + $result->endTest($test, 0); + } + + $this->tearDown(); + + $result->endTestSuite($this); + + return $result; + } + + public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void + { + $this->runTestInSeparateProcess = $runTestInSeparateProcess; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + /** + * Returns the test at the given index. + * + * @return false|Test + */ + public function testAt(int $index) + { + if (isset($this->tests[$index])) { + return $this->tests[$index]; + } + + return false; + } + + /** + * Returns the tests as an enumeration. + */ + public function tests(): array + { + return $this->tests; + } + + /** + * Set tests of the test suite + */ + public function setTests(array $tests): void + { + $this->tests = $tests; + } + + /** + * Mark the test suite as skipped. + * + * @param string $message + * + * @throws SkippedTestSuiteError + */ + public function markTestSuiteSkipped($message = ''): void + { + throw new SkippedTestSuiteError($message); + } + + /** + * @param bool $beStrictAboutChangesToGlobalState + */ + public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState): void + { + if (null === $this->beStrictAboutChangesToGlobalState && \is_bool($beStrictAboutChangesToGlobalState)) { + $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; + } + } + + /** + * @param bool $backupGlobals + */ + public function setBackupGlobals($backupGlobals): void + { + if (null === $this->backupGlobals && \is_bool($backupGlobals)) { + $this->backupGlobals = $backupGlobals; + } + } + + /** + * @param bool $backupStaticAttributes + */ + public function setBackupStaticAttributes($backupStaticAttributes): void + { + if (null === $this->backupStaticAttributes && \is_bool($backupStaticAttributes)) { + $this->backupStaticAttributes = $backupStaticAttributes; + } + } + + /** + * Returns an iterator for this test suite. + */ + public function getIterator(): Iterator + { + $iterator = new TestSuiteIterator($this); + + if ($this->iteratorFilter !== null) { + $iterator = $this->iteratorFilter->factory($iterator, $this); + } + + return $iterator; + } + + public function injectFilter(Factory $filter): void + { + $this->iteratorFilter = $filter; + + foreach ($this as $test) { + if ($test instanceof self) { + $test->injectFilter($filter); + } + } + } + + /** + * Creates a default TestResult object. + */ + protected function createResult(): TestResult + { + return new TestResult; + } + + /** + * @throws Exception + */ + protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method): void + { + if (!$this->isTestMethod($method)) { + return; + } + + $name = $method->getName(); + + if (!$method->isPublic()) { + $this->addTest( + self::warning( + \sprintf( + 'Test method "%s" in test class "%s" is not public.', + $name, + $class->getName() + ) + ) + ); + + return; + } + + $test = self::createTest($class, $name); + + if ($test instanceof TestCase || $test instanceof DataProviderTestSuite) { + $test->setDependencies( + \PHPUnit\Util\Test::getDependencies($class->getName(), $name) + ); + } + + $this->addTest( + $test, + \PHPUnit\Util\Test::getGroups($class->getName(), $name) + ); + } + + /** + * @param string $message + */ + protected static function warning($message): WarningTestCase + { + return new WarningTestCase($message); + } + + /** + * @param string $class + * @param string $methodName + * @param string $message + */ + protected static function skipTest($class, $methodName, $message): SkippedTestCase + { + return new SkippedTestCase($class, $methodName, $message); + } + + /** + * @param string $class + * @param string $methodName + * @param string $message + */ + protected static function incompleteTest($class, $methodName, $message): IncompleteTestCase + { + return new IncompleteTestCase($class, $methodName, $message); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Util\Filter; +use Throwable; + +/** + * Wraps Exceptions thrown by code under test. + * + * Re-instantiates Exceptions thrown by user-space code to retain their original + * class names, properties, and stack traces (but without arguments). + * + * Unlike PHPUnit\Framework_\Exception, the complete stack of previous Exceptions + * is processed. + */ +class ExceptionWrapper extends Exception +{ + /** + * @var string + */ + protected $className; + + /** + * @var null|ExceptionWrapper + */ + protected $previous; + + public function __construct(Throwable $t) + { + // PDOException::getCode() is a string. + // @see https://php.net/manual/en/class.pdoexception.php#95812 + parent::__construct($t->getMessage(), (int) $t->getCode()); + $this->setOriginalException($t); + } + + /** + * @throws \InvalidArgumentException + */ + public function __toString(): string + { + $string = TestFailure::exceptionToString($this); + + if ($trace = Filter::getFilteredStacktrace($this)) { + $string .= "\n" . $trace; + } + + if ($this->previous) { + $string .= "\nCaused by\n" . $this->previous; + } + + return $string; + } + + public function getClassName(): string + { + return $this->className; + } + + public function getPreviousWrapped(): ?self + { + return $this->previous; + } + + public function setClassName(string $className): void + { + $this->className = $className; + } + + public function setOriginalException(\Throwable $t): void + { + $this->originalException($t); + + $this->className = \get_class($t); + $this->file = $t->getFile(); + $this->line = $t->getLine(); + + $this->serializableTrace = $t->getTrace(); + + foreach ($this->serializableTrace as $i => $call) { + unset($this->serializableTrace[$i]['args']); + } + + if ($t->getPrevious()) { + $this->previous = new self($t->getPrevious()); + } + } + + public function getOriginalException(): ?Throwable + { + return $this->originalException(); + } + + /** + * Method to contain static originalException to exclude it from stacktrace to prevent the stacktrace contents, + * which can be quite big, from being garbage-collected, thus blocking memory until shutdown. + * Approach works both for var_dump() and var_export() and print_r() + */ + private function originalException(Throwable $exceptionToStore = null): ?Throwable + { + static $originalExceptions; + + $instanceId = \spl_object_hash($this); + + if ($exceptionToStore) { + $originalExceptions[$instanceId] = $exceptionToStore; + } + + return $originalExceptions[$instanceId] ?? null; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * Thrown when there is a warning. + */ +class Warning extends Exception implements SelfDescribing +{ + /** + * Wrapper for getMessage() which is declared as final. + */ + public function toString(): string + { + return $this->getMessage(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +class SkippedTestError extends AssertionFailedError implements SkippedTest +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Exception for expectations which failed their check. + * + * The exception contains the error message and optionally a + * SebastianBergmann\Comparator\ComparisonFailure which is used to + * generate diff output of the failed expectations. + */ +class ExpectationFailedException extends AssertionFailedError +{ + /** + * @var ComparisonFailure + */ + protected $comparisonFailure; + + public function __construct(string $message, ComparisonFailure $comparisonFailure = null, \Exception $previous = null) + { + $this->comparisonFailure = $comparisonFailure; + + parent::__construct($message, 0, $previous); + } + + public function getComparisonFailure(): ?ComparisonFailure + { + return $this->comparisonFailure; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +use PHPUnit\Framework\Error\Error; +use Throwable; + +/** + * A TestFailure collects a failed test together with the caught exception. + */ +class TestFailure +{ + /** + * @var null|Test + */ + protected $failedTest; + + /** + * @var Throwable + */ + protected $thrownException; + + /** + * @var string + */ + private $testName; + + /** + * Returns a description for an exception. + * + * @throws \InvalidArgumentException + */ + public static function exceptionToString(Throwable $e): string + { + if ($e instanceof SelfDescribing) { + $buffer = $e->toString(); + + if ($e instanceof ExpectationFailedException && $e->getComparisonFailure()) { + $buffer .= $e->getComparisonFailure()->getDiff(); + } + + if (!empty($buffer)) { + $buffer = \trim($buffer) . "\n"; + } + + return $buffer; + } + + if ($e instanceof Error) { + return $e->getMessage() . "\n"; + } + + if ($e instanceof ExceptionWrapper) { + return $e->getClassName() . ': ' . $e->getMessage() . "\n"; + } + + return \get_class($e) . ': ' . $e->getMessage() . "\n"; + } + + /** + * Constructs a TestFailure with the given test and exception. + * + * @param Throwable $t + */ + public function __construct(Test $failedTest, $t) + { + if ($failedTest instanceof SelfDescribing) { + $this->testName = $failedTest->toString(); + } else { + $this->testName = \get_class($failedTest); + } + + if (!$failedTest instanceof TestCase || !$failedTest->isInIsolation()) { + $this->failedTest = $failedTest; + } + + $this->thrownException = $t; + } + + /** + * Returns a short description of the failure. + */ + public function toString(): string + { + return \sprintf( + '%s: %s', + $this->testName, + $this->thrownException->getMessage() + ); + } + + /** + * Returns a description for the thrown exception. + * + * @throws \InvalidArgumentException + */ + public function getExceptionAsString(): string + { + return self::exceptionToString($this->thrownException); + } + + /** + * Returns the name of the failing test (including data set, if any). + */ + public function getTestName(): string + { + return $this->testName; + } + + /** + * Returns the failing test. + * + * Note: The test object is not set when the test is executed in process + * isolation. + * + * @see Exception + */ + public function failedTest(): ?Test + { + return $this->failedTest; + } + + /** + * Gets the thrown exception. + */ + public function thrownException(): Throwable + { + return $this->thrownException; + } + + /** + * Returns the exception's message. + */ + public function exceptionMessage(): string + { + return $this->thrownException()->getMessage(); + } + + /** + * Returns true if the thrown exception + * is of type AssertionFailedError. + */ + public function isFailure(): bool + { + return $this->thrownException() instanceof AssertionFailedError; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework; + +/** + * Extension to PHPUnit\Framework\AssertionFailedError to mark the special + * case of a test that unintentionally covers code. + */ +class UnintentionallyCoveredCodeError extends RiskyTestError +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use DOMCharacterData; +use DOMDocument; +use DOMElement; +use DOMNode; +use DOMText; +use PHPUnit\Framework\Exception; +use ReflectionClass; + +final class Xml +{ + public static function import(DOMElement $element): DOMElement + { + $document = new DOMDocument; + + return $document->importNode($element, true); + } + + /** + * Load an $actual document into a DOMDocument. This is called + * from the selector assertions. + * + * If $actual is already a DOMDocument, it is returned with + * no changes. Otherwise, $actual is loaded into a new DOMDocument + * as either HTML or XML, depending on the value of $isHtml. If $isHtml is + * false and $xinclude is true, xinclude is performed on the loaded + * DOMDocument. + * + * Note: prior to PHPUnit 3.3.0, this method loaded a file and + * not a string as it currently does. To load a file into a + * DOMDocument, use loadFile() instead. + * + * @param DOMDocument|string $actual + * + * @throws Exception + */ + public static function load($actual, bool $isHtml = false, string $filename = '', bool $xinclude = false, bool $strict = false): DOMDocument + { + if ($actual instanceof DOMDocument) { + return $actual; + } + + if (!\is_string($actual)) { + throw new Exception('Could not load XML from ' . \gettype($actual)); + } + + if ($actual === '') { + throw new Exception('Could not load XML from empty string'); + } + + // Required for XInclude on Windows. + if ($xinclude) { + $cwd = \getcwd(); + @\chdir(\dirname($filename)); + } + + $document = new DOMDocument; + $document->preserveWhiteSpace = false; + + $internal = \libxml_use_internal_errors(true); + $message = ''; + $reporting = \error_reporting(0); + + if ($filename !== '') { + // Required for XInclude + $document->documentURI = $filename; + } + + if ($isHtml) { + $loaded = $document->loadHTML($actual); + } else { + $loaded = $document->loadXML($actual); + } + + if (!$isHtml && $xinclude) { + $document->xinclude(); + } + + foreach (\libxml_get_errors() as $error) { + $message .= "\n" . $error->message; + } + + \libxml_use_internal_errors($internal); + \error_reporting($reporting); + + if (isset($cwd)) { + @\chdir($cwd); + } + + if ($loaded === false || ($strict && $message !== '')) { + if ($filename !== '') { + throw new Exception( + \sprintf( + 'Could not load "%s".%s', + $filename, + $message !== '' ? "\n" . $message : '' + ) + ); + } + + if ($message === '') { + $message = 'Could not load XML for unknown reason'; + } + + throw new Exception($message); + } + + return $document; + } + + /** + * Loads an XML (or HTML) file into a DOMDocument object. + * + * @throws Exception + */ + public static function loadFile(string $filename, bool $isHtml = false, bool $xinclude = false, bool $strict = false): DOMDocument + { + $reporting = \error_reporting(0); + $contents = \file_get_contents($filename); + + \error_reporting($reporting); + + if ($contents === false) { + throw new Exception( + \sprintf( + 'Could not read "%s".', + $filename + ) + ); + } + + return self::load($contents, $isHtml, $filename, $xinclude, $strict); + } + + public static function removeCharacterDataNodes(DOMNode $node): void + { + if ($node->hasChildNodes()) { + for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { + if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { + $node->removeChild($child); + } + } + } + } + + /** + * Escapes a string for the use in XML documents + * + * Any Unicode character is allowed, excluding the surrogate blocks, FFFE, + * and FFFF (not even as character reference). + * + * @see https://www.w3.org/TR/xml/#charsets + */ + public static function prepareString(string $string): string + { + return \preg_replace( + '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', + '', + \htmlspecialchars( + self::convertToUtf8($string), + \ENT_QUOTES + ) + ); + } + + /** + * "Convert" a DOMElement object into a PHP variable. + */ + public static function xmlToVariable(DOMElement $element) + { + $variable = null; + + switch ($element->tagName) { + case 'array': + $variable = []; + + foreach ($element->childNodes as $entry) { + if (!$entry instanceof DOMElement || $entry->tagName !== 'element') { + continue; + } + $item = $entry->childNodes->item(0); + + if ($item instanceof DOMText) { + $item = $entry->childNodes->item(1); + } + + $value = self::xmlToVariable($item); + + if ($entry->hasAttribute('key')) { + $variable[(string) $entry->getAttribute('key')] = $value; + } else { + $variable[] = $value; + } + } + + break; + + case 'object': + $className = $element->getAttribute('class'); + + if ($element->hasChildNodes()) { + $arguments = $element->childNodes->item(0)->childNodes; + $constructorArgs = []; + + foreach ($arguments as $argument) { + if ($argument instanceof DOMElement) { + $constructorArgs[] = self::xmlToVariable($argument); + } + } + + $class = new ReflectionClass($className); + $variable = $class->newInstanceArgs($constructorArgs); + } else { + $variable = new $className; + } + + break; + + case 'boolean': + $variable = $element->textContent === 'true'; + + break; + + case 'integer': + case 'double': + case 'string': + $variable = $element->textContent; + + \settype($variable, $element->tagName); + + break; + } + + return $variable; + } + + private static function convertToUtf8(string $string): string + { + if (!self::isUtf8($string)) { + $string = \mb_convert_encoding($string, 'UTF-8'); + } + + return $string; + } + + private static function isUtf8(string $string): bool + { + $length = \strlen($string); + + for ($i = 0; $i < $length; $i++) { + if (\ord($string[$i]) < 0x80) { + $n = 0; + } elseif ((\ord($string[$i]) & 0xE0) === 0xC0) { + $n = 1; + } elseif ((\ord($string[$i]) & 0xF0) === 0xE0) { + $n = 2; + } elseif ((\ord($string[$i]) & 0xF0) === 0xF0) { + $n = 3; + } else { + return false; + } + + for ($j = 0; $j < $n; $j++) { + if ((++$i === $length) || ((\ord($string[$i]) & 0xC0) !== 0x80)) { + return false; + } + } + } + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +final class ConfigurationGenerator +{ + /** + * @var string + */ + private const TEMPLATE = << + + + + {tests_directory} + + + + + + {src_directory} + + + + +EOT; + + public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory): string + { + return \str_replace( + [ + '{phpunit_version}', + '{bootstrap_script}', + '{tests_directory}', + '{src_directory}', + ], + [ + $phpunitVersion, + $bootstrapScript, + $testsDirectory, + $srcDirectory, + ], + self::TEMPLATE + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use Closure; + +final class GlobalState +{ + /** + * @var string[] + */ + private const SUPER_GLOBAL_ARRAYS = [ + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST', + ]; + + public static function getIncludedFilesAsString(): string + { + return static::processIncludedFilesAsString(\get_included_files()); + } + + /** + * @param string[] $files + */ + public static function processIncludedFilesAsString(array $files): string + { + $blacklist = new Blacklist; + $prefix = false; + $result = ''; + + if (\defined('__PHPUNIT_PHAR__')) { + $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; + } + + for ($i = \count($files) - 1; $i > 0; $i--) { + $file = $files[$i]; + + if (!empty($GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST']) && + \in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'])) { + continue; + } + + if ($prefix !== false && \strpos($file, $prefix) === 0) { + continue; + } + + // Skip virtual file system protocols + if (\preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { + continue; + } + + if (!$blacklist->isBlacklisted($file) && \is_file($file)) { + $result = 'require_once \'' . $file . "';\n" . $result; + } + } + + return $result; + } + + public static function getIniSettingsAsString(): string + { + $result = ''; + $iniSettings = \ini_get_all(null, false); + + foreach ($iniSettings as $key => $value) { + $result .= \sprintf( + '@ini_set(%s, %s);' . "\n", + self::exportVariable($key), + self::exportVariable($value) + ); + } + + return $result; + } + + public static function getConstantsAsString(): string + { + $constants = \get_defined_constants(true); + $result = ''; + + if (isset($constants['user'])) { + foreach ($constants['user'] as $name => $value) { + $result .= \sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + self::exportVariable($value) + ); + } + } + + return $result; + } + + public static function getGlobalsAsString(): string + { + $result = ''; + + foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) { + if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { + foreach (\array_keys($GLOBALS[$superGlobalArray]) as $key) { + if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { + continue; + } + + $result .= \sprintf( + '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", + $superGlobalArray, + $key, + self::exportVariable($GLOBALS[$superGlobalArray][$key]) + ); + } + } + } + + $blacklist = self::SUPER_GLOBAL_ARRAYS; + $blacklist[] = 'GLOBALS'; + + foreach (\array_keys($GLOBALS) as $key) { + if (!$GLOBALS[$key] instanceof Closure && !\in_array($key, $blacklist, true)) { + $result .= \sprintf( + '$GLOBALS[\'%s\'] = %s;' . "\n", + $key, + self::exportVariable($GLOBALS[$key]) + ); + } + } + + return $result; + } + + private static function exportVariable($variable): string + { + if (\is_scalar($variable) || $variable === null || + (\is_array($variable) && self::arrayOnlyContainsScalars($variable))) { + return \var_export($variable, true); + } + + return 'unserialize(' . \var_export(\serialize($variable), true) . ')'; + } + + private static function arrayOnlyContainsScalars(array $array): bool + { + $result = true; + + foreach ($array as $element) { + if (\is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!\is_scalar($element) && $element !== null) { + $result = false; + } + + if ($result === false) { + break; + } + } + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use __PHP_Incomplete_Class; +use ErrorException; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\SyntheticError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestFailure; +use PHPUnit\Framework\TestResult; +use SebastianBergmann\Environment\Runtime; + +/** + * Utility methods for PHP sub-processes. + */ +abstract class AbstractPhpProcess +{ + /** + * @var Runtime + */ + protected $runtime; + + /** + * @var bool + */ + protected $stderrRedirection = false; + + /** + * @var string + */ + protected $stdin = ''; + + /** + * @var string + */ + protected $args = ''; + + /** + * @var array + */ + protected $env = []; + + /** + * @var int + */ + protected $timeout = 0; + + public static function factory(): self + { + if (\DIRECTORY_SEPARATOR === '\\') { + return new WindowsPhpProcess; + } + + return new DefaultPhpProcess; + } + + public function __construct() + { + $this->runtime = new Runtime; + } + + /** + * Defines if should use STDERR redirection or not. + * + * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. + */ + public function setUseStderrRedirection(bool $stderrRedirection): void + { + $this->stderrRedirection = $stderrRedirection; + } + + /** + * Returns TRUE if uses STDERR redirection or FALSE if not. + */ + public function useStderrRedirection(): bool + { + return $this->stderrRedirection; + } + + /** + * Sets the input string to be sent via STDIN + */ + public function setStdin(string $stdin): void + { + $this->stdin = $stdin; + } + + /** + * Returns the input string to be sent via STDIN + */ + public function getStdin(): string + { + return $this->stdin; + } + + /** + * Sets the string of arguments to pass to the php job + */ + public function setArgs(string $args): void + { + $this->args = $args; + } + + /** + * Returns the string of arguments to pass to the php job + */ + public function getArgs(): string + { + return $this->args; + } + + /** + * Sets the array of environment variables to start the child process with + * + * @param array $env + */ + public function setEnv(array $env): void + { + $this->env = $env; + } + + /** + * Returns the array of environment variables to start the child process with + */ + public function getEnv(): array + { + return $this->env; + } + + /** + * Sets the amount of seconds to wait before timing out + */ + public function setTimeout(int $timeout): void + { + $this->timeout = $timeout; + } + + /** + * Returns the amount of seconds to wait before timing out + */ + public function getTimeout(): int + { + return $this->timeout; + } + + /** + * Runs a single test in a separate PHP process. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function runTestJob(string $job, Test $test, TestResult $result): void + { + $result->startTest($test); + + $_result = $this->runJob($job); + + $this->processChildResult( + $test, + $result, + $_result['stdout'], + $_result['stderr'] + ); + } + + /** + * Returns the command based into the configurations. + */ + public function getCommand(array $settings, string $file = null): string + { + $command = $this->runtime->getBinary(); + $command .= $this->settingsToParameters($settings); + + if (\PHP_SAPI === 'phpdbg') { + $command .= ' -qrr'; + + if (!$file) { + $command .= 's='; + } + } + + if ($file) { + $command .= ' ' . \escapeshellarg($file); + } + + if ($this->args) { + if (!$file) { + $command .= ' --'; + } + $command .= ' ' . $this->args; + } + + if ($this->stderrRedirection === true) { + $command .= ' 2>&1'; + } + + return $command; + } + + /** + * Runs a single job (PHP code) using a separate PHP process. + */ + abstract public function runJob(string $job, array $settings = []): array; + + protected function settingsToParameters(array $settings): string + { + $buffer = ''; + + foreach ($settings as $setting) { + $buffer .= ' -d ' . \escapeshellarg($setting); + } + + return $buffer; + } + + /** + * Processes the TestResult object from an isolated process. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr): void + { + $time = 0; + + if (!empty($stderr)) { + $result->addError( + $test, + new Exception(\trim($stderr)), + $time + ); + } else { + \set_error_handler(function ($errno, $errstr, $errfile, $errline): void { + throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); + }); + + try { + if (\strpos($stdout, "#!/usr/bin/env php\n") === 0) { + $stdout = \substr($stdout, 19); + } + + $childResult = \unserialize(\str_replace("#!/usr/bin/env php\n", '', $stdout)); + \restore_error_handler(); + } catch (ErrorException $e) { + \restore_error_handler(); + $childResult = false; + + $result->addError( + $test, + new Exception(\trim($stdout), 0, $e), + $time + ); + } + + if ($childResult !== false) { + if (!empty($childResult['output'])) { + $output = $childResult['output']; + } + + /* @var TestCase $test */ + + $test->setResult($childResult['testResult']); + $test->addToAssertionCount($childResult['numAssertions']); + + /** @var TestResult $childResult */ + $childResult = $childResult['result']; + + if ($result->getCollectCodeCoverageInformation()) { + $result->getCodeCoverage()->merge( + $childResult->getCodeCoverage() + ); + } + + $time = $childResult->time(); + $notImplemented = $childResult->notImplemented(); + $risky = $childResult->risky(); + $skipped = $childResult->skipped(); + $errors = $childResult->errors(); + $warnings = $childResult->warnings(); + $failures = $childResult->failures(); + + if (!empty($notImplemented)) { + $result->addError( + $test, + $this->getException($notImplemented[0]), + $time + ); + } elseif (!empty($risky)) { + $result->addError( + $test, + $this->getException($risky[0]), + $time + ); + } elseif (!empty($skipped)) { + $result->addError( + $test, + $this->getException($skipped[0]), + $time + ); + } elseif (!empty($errors)) { + $result->addError( + $test, + $this->getException($errors[0]), + $time + ); + } elseif (!empty($warnings)) { + $result->addWarning( + $test, + $this->getException($warnings[0]), + $time + ); + } elseif (!empty($failures)) { + $result->addFailure( + $test, + $this->getException($failures[0]), + $time + ); + } + } + } + + $result->endTest($test, $time); + + if (!empty($output)) { + print $output; + } + } + + /** + * Gets the thrown exception from a PHPUnit\Framework\TestFailure. + * + * @see https://github.com/sebastianbergmann/phpunit/issues/74 + */ + private function getException(TestFailure $error): Exception + { + $exception = $error->thrownException(); + + if ($exception instanceof __PHP_Incomplete_Class) { + $exceptionArray = []; + + foreach ((array) $exception as $key => $value) { + $key = \substr($key, \strrpos($key, "\0") + 1); + $exceptionArray[$key] = $value; + } + + $exception = new SyntheticError( + \sprintf( + '%s: %s', + $exceptionArray['_PHP_Incomplete_Class_Name'], + $exceptionArray['message'] + ), + $exceptionArray['code'], + $exceptionArray['file'], + $exceptionArray['line'], + $exceptionArray['trace'] + ); + } + + return $exception; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use PHPUnit\Framework\Exception; + +/** + * Windows utility for PHP sub-processes. + * + * Reading from STDOUT or STDERR hangs forever on Windows if the output is + * too large. + * + * @see https://bugs.php.net/bug.php?id=51800 + */ +class WindowsPhpProcess extends DefaultPhpProcess +{ + public function getCommand(array $settings, string $file = null): string + { + return '"' . parent::getCommand($settings, $file) . '"'; + } + + protected function getHandles(): array + { + if (false === $stdout_handle = \tmpfile()) { + throw new Exception( + 'A temporary file could not be created; verify that your TEMP environment variable is writable' + ); + } + + return [ + 1 => $stdout_handle, + ]; + } + + protected function useTemporaryFile(): bool + { + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\PHP; + +use PHPUnit\Framework\Exception; + +/** + * Default utility for PHP sub-processes. + */ +class DefaultPhpProcess extends AbstractPhpProcess +{ + /** + * @var string + */ + protected $tempFile; + + /** + * Runs a single job (PHP code) using a separate PHP process. + * + * @throws Exception + */ + public function runJob(string $job, array $settings = []): array + { + if ($this->useTemporaryFile() || $this->stdin) { + if (!($this->tempFile = \tempnam(\sys_get_temp_dir(), 'PHPUnit')) || + \file_put_contents($this->tempFile, $job) === false) { + throw new Exception( + 'Unable to write temporary file' + ); + } + + $job = $this->stdin; + } + + return $this->runProcess($job, $settings); + } + + /** + * Returns an array of file handles to be used in place of pipes + */ + protected function getHandles(): array + { + return []; + } + + /** + * Handles creating the child process and returning the STDOUT and STDERR + * + * @throws Exception + */ + protected function runProcess(string $job, array $settings): array + { + $handles = $this->getHandles(); + + $env = null; + + if ($this->env) { + $env = $_SERVER ?? []; + unset($env['argv'], $env['argc']); + $env = \array_merge($env, $this->env); + + foreach ($env as $envKey => $envVar) { + if (\is_array($envVar)) { + unset($env[$envKey]); + } + } + } + + $pipeSpec = [ + 0 => $handles[0] ?? ['pipe', 'r'], + 1 => $handles[1] ?? ['pipe', 'w'], + 2 => $handles[2] ?? ['pipe', 'w'], + ]; + + $process = \proc_open( + $this->getCommand($settings, $this->tempFile), + $pipeSpec, + $pipes, + null, + $env + ); + + if (!\is_resource($process)) { + throw new Exception( + 'Unable to spawn worker process' + ); + } + + if ($job) { + $this->process($pipes[0], $job); + } + + \fclose($pipes[0]); + + $stderr = $stdout = ''; + + if ($this->timeout) { + unset($pipes[0]); + + while (true) { + $r = $pipes; + $w = null; + $e = null; + + $n = @\stream_select($r, $w, $e, $this->timeout); + + if ($n === false) { + break; + } + + if ($n === 0) { + \proc_terminate($process, 9); + + throw new Exception( + \sprintf( + 'Job execution aborted after %d seconds', + $this->timeout + ) + ); + } + + if ($n > 0) { + foreach ($r as $pipe) { + $pipeOffset = 0; + + foreach ($pipes as $i => $origPipe) { + if ($pipe === $origPipe) { + $pipeOffset = $i; + + break; + } + } + + if (!$pipeOffset) { + break; + } + + $line = \fread($pipe, 8192); + + if ($line === '') { + \fclose($pipes[$pipeOffset]); + + unset($pipes[$pipeOffset]); + } else { + if ($pipeOffset === 1) { + $stdout .= $line; + } else { + $stderr .= $line; + } + } + } + + if (empty($pipes)) { + break; + } + } + } + } else { + if (isset($pipes[1])) { + $stdout = \stream_get_contents($pipes[1]); + + \fclose($pipes[1]); + } + + if (isset($pipes[2])) { + $stderr = \stream_get_contents($pipes[2]); + + \fclose($pipes[2]); + } + } + + if (isset($handles[1])) { + \rewind($handles[1]); + + $stdout = \stream_get_contents($handles[1]); + + \fclose($handles[1]); + } + + if (isset($handles[2])) { + \rewind($handles[2]); + + $stderr = \stream_get_contents($handles[2]); + + \fclose($handles[2]); + } + + \proc_close($process); + + $this->cleanup(); + + return ['stdout' => $stdout, 'stderr' => $stderr]; + } + + protected function process($pipe, string $job): void + { + \fwrite($pipe, $job); + } + + protected function cleanup(): void + { + if ($this->tempFile) { + \unlink($this->tempFile); + } + } + + protected function useTemporaryFile(): bool + { + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +eval('?>' . \file_get_contents('php://stdin')); +setCodeCoverage( + new CodeCoverage( + null, + unserialize('{codeCoverageFilter}') + ) + ); + } + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->enforceTimeLimit({enforcesTimeLimit}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); + + $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(TRUE); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); + } + + ini_set('xdebug.scream', 0); + @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ + if ($stdout = stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } + } + + print serialize( + [ + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ] + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = PHPUnit\Util\Configuration::getInstance($configurationFilePath); + $configuration->handlePHPConfiguration(); + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); +setCodeCoverage( + new CodeCoverage( + null, + unserialize('{codeCoverageFilter}') + ) + ); + } + + $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); + $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); + $result->enforceTimeLimit({enforcesTimeLimit}); + $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); + $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); + + /** @var TestCase $test */ + $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); + $test->setDependencyInput(unserialize('{dependencyInput}')); + $test->setInIsolation(TRUE); + + ob_end_clean(); + $test->run($result); + $output = ''; + if (!$test->hasExpectationOnOutput()) { + $output = $test->getActualOutput(); + } + + ini_set('xdebug.scream', '0'); + @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ + if ($stdout = stream_get_contents(STDOUT)) { + $output = $stdout . $output; + $streamMetaData = stream_get_meta_data(STDOUT); + if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { + @ftruncate(STDOUT, 0); + @rewind(STDOUT); + } + } + + print serialize( + [ + 'testResult' => $test->getResult(), + 'numAssertions' => $test->getNumAssertions(), + 'result' => $result, + 'output' => $output + ] + ); +} + +$configurationFilePath = '{configurationFilePath}'; + +if ('' !== $configurationFilePath) { + $configuration = PHPUnit\Util\Configuration::getInstance($configurationFilePath); + $configuration->handlePHPConfiguration(); + unset($configuration); +} + +function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) +{ + return true; +} + +set_error_handler('__phpunit_error_handler'); + +{constants} +{included_files} +{globals} + +restore_error_handler(); + +if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { + require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; + unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); +} + +__phpunit_run_isolated_test(); +start(__FILE__); +} + +register_shutdown_function(function() use ($coverage) { + $output = null; + if ($coverage) { + $output = $coverage->stop(); + } + file_put_contents('{coverageFile}', serialize($output)); +}); + +ob_end_clean(); + +require '{job}'; + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +final class RegularExpression +{ + /** + * @throws \Exception + * + * @return false|int + */ + public static function safeMatch(string $pattern, string $subject, ?array $matches = null, int $flags = 0, int $offset = 0) + { + $handler_terminator = ErrorHandler::handleErrorOnce(); + $match = \preg_match($pattern, $subject, $matches, $flags, $offset); + $handler_terminator(); + + return $match; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PharIo\Version\VersionConstraintParser; +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\CodeCoverageException; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\InvalidCoversTargetException; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Framework\SkippedTestError; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\Warning; +use PHPUnit\Runner\Version; +use ReflectionClass; +use ReflectionException; +use ReflectionFunction; +use ReflectionMethod; +use SebastianBergmann\Environment\OperatingSystem; +use Traversable; + +final class Test +{ + /** + * @var int + */ + public const UNKNOWN = -1; + + /** + * @var int + */ + public const SMALL = 0; + + /** + * @var int + */ + public const MEDIUM = 1; + + /** + * @var int + */ + public const LARGE = 2; + + /** + * @var string + * + * @todo This constant should be private (it's public because of TestTest::testGetProvidedDataRegEx) + */ + public const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/'; + + /** + * @var string + */ + private const REGEX_TEST_WITH = '/@testWith\s+/'; + + /** + * @var string + */ + private const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m'; + + /** + * @var string + */ + private const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; + + /** + * @var string + */ + private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\t -.|~^]+)[ \t]*\r?$/m'; + + /** + * @var string + */ + private const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m'; + + /** + * @var string + */ + private const REGEX_REQUIRES_SETTING = '/@requires\s+(?Psetting)\s+(?P([^ ]+?))\s*(?P[\w\.-]+[\w\.]?)?[ \t]*\r?$/m'; + + /** + * @var string + */ + private const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^\s<>=!]+))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; + + /** + * @var array + */ + private static $annotationCache = []; + + /** + * @var array + */ + private static $hookMethods = []; + + public static function describe(\PHPUnit\Framework\Test $test): array + { + if ($test instanceof TestCase) { + return [\get_class($test), $test->getName()]; + } + + if ($test instanceof SelfDescribing) { + return ['', $test->toString()]; + } + + return ['', \get_class($test)]; + } + + public static function describeAsString(\PHPUnit\Framework\Test $test): string + { + if ($test instanceof SelfDescribing) { + return $test->toString(); + } + + return \get_class($test); + } + + /** + * @throws CodeCoverageException + * + * @return array|bool + */ + public static function getLinesToBeCovered(string $className, string $methodName) + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + if (self::shouldCoversAnnotationBeUsed($annotations) === false) { + return false; + } + + return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers'); + } + + /** + * Returns lines of code specified with the @uses annotation. + * + * @throws CodeCoverageException + */ + public static function getLinesToBeUsed(string $className, string $methodName): array + { + return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); + } + + /** + * Returns the requirements for a test. + * + * @throws Warning + */ + public static function getRequirements(string $className, string $methodName): array + { + $reflector = new ReflectionClass($className); + $docComment = $reflector->getDocComment(); + $reflector = new ReflectionMethod($className, $methodName); + $docComment .= "\n" . $reflector->getDocComment(); + $requires = []; + + if ($count = \preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) { + foreach (\range(0, $count - 1) as $i) { + $requires[$matches['name'][$i]] = $matches['value'][$i]; + } + } + + if ($count = \preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) { + foreach (\range(0, $count - 1) as $i) { + $requires[$matches['name'][$i]] = [ + 'version' => $matches['version'][$i], + 'operator' => $matches['operator'][$i], + ]; + } + } + + if ($count = \preg_match_all(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $docComment, $matches)) { + foreach (\range(0, $count - 1) as $i) { + if (!empty($requires[$matches['name'][$i]])) { + continue; + } + + try { + $versionConstraintParser = new VersionConstraintParser; + + $requires[$matches['name'][$i] . '_constraint'] = [ + 'constraint' => $versionConstraintParser->parse(\trim($matches['constraint'][$i])), + ]; + } catch (\PharIo\Version\Exception $e) { + throw new Warning($e->getMessage(), $e->getCode(), $e); + } + } + } + + if ($count = \preg_match_all(self::REGEX_REQUIRES_SETTING, $docComment, $matches)) { + $requires['setting'] = []; + + foreach (\range(0, $count - 1) as $i) { + $requires['setting'][$matches['setting'][$i]] = $matches['value'][$i]; + } + } + + if ($count = \preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) { + foreach (\range(0, $count - 1) as $i) { + $name = $matches['name'][$i] . 's'; + + if (!isset($requires[$name])) { + $requires[$name] = []; + } + + $requires[$name][] = $matches['value'][$i]; + + if ($name !== 'extensions' || empty($matches['version'][$i])) { + continue; + } + + $requires['extension_versions'][$matches['value'][$i]] = [ + 'version' => $matches['version'][$i], + 'operator' => $matches['operator'][$i], + ]; + } + } + + return $requires; + } + + /** + * Returns the missing requirements for a test. + * + * @throws Warning + * + * @return string[] + */ + public static function getMissingRequirements(string $className, string $methodName): array + { + $required = static::getRequirements($className, $methodName); + $missing = []; + + if (!empty($required['PHP'])) { + $operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']; + + if (!\version_compare(\PHP_VERSION, $required['PHP']['version'], $operator)) { + $missing[] = \sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']); + } + } elseif (!empty($required['PHP_constraint'])) { + $version = new \PharIo\Version\Version(self::sanitizeVersionNumber(\PHP_VERSION)); + + if (!$required['PHP_constraint']['constraint']->complies($version)) { + $missing[] = \sprintf( + 'PHP version does not match the required constraint %s.', + $required['PHP_constraint']['constraint']->asString() + ); + } + } + + if (!empty($required['PHPUnit'])) { + $phpunitVersion = Version::id(); + + $operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']; + + if (!\version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) { + $missing[] = \sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']); + } + } elseif (!empty($required['PHPUnit_constraint'])) { + $phpunitVersion = new \PharIo\Version\Version(self::sanitizeVersionNumber(Version::id())); + + if (!$required['PHPUnit_constraint']['constraint']->complies($phpunitVersion)) { + $missing[] = \sprintf( + 'PHPUnit version does not match the required constraint %s.', + $required['PHPUnit_constraint']['constraint']->asString() + ); + } + } + + if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem)->getFamily()) { + $missing[] = \sprintf('Operating system %s is required.', $required['OSFAMILY']); + } + + if (!empty($required['OS'])) { + $requiredOsPattern = \sprintf('/%s/i', \addcslashes($required['OS'], '/')); + + if (!\preg_match($requiredOsPattern, \PHP_OS)) { + $missing[] = \sprintf('Operating system matching %s is required.', $requiredOsPattern); + } + } + + if (!empty($required['functions'])) { + foreach ($required['functions'] as $function) { + $pieces = \explode('::', $function); + + if (\count($pieces) === 2 && \method_exists($pieces[0], $pieces[1])) { + continue; + } + + if (\function_exists($function)) { + continue; + } + + $missing[] = \sprintf('Function %s is required.', $function); + } + } + + if (!empty($required['setting'])) { + foreach ($required['setting'] as $setting => $value) { + if (\ini_get($setting) != $value) { + $missing[] = \sprintf('Setting "%s" must be "%s".', $setting, $value); + } + } + } + + if (!empty($required['extensions'])) { + foreach ($required['extensions'] as $extension) { + if (isset($required['extension_versions'][$extension])) { + continue; + } + + if (!\extension_loaded($extension)) { + $missing[] = \sprintf('Extension %s is required.', $extension); + } + } + } + + if (!empty($required['extension_versions'])) { + foreach ($required['extension_versions'] as $extension => $required) { + $actualVersion = \phpversion($extension); + + $operator = empty($required['operator']) ? '>=' : $required['operator']; + + if ($actualVersion === false || !\version_compare($actualVersion, $required['version'], $operator)) { + $missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']); + } + } + } + + return $missing; + } + + /** + * Returns the expected exception for a test. + * + * @return array|false + */ + public static function getExpectedException(string $className, ?string $methodName) + { + $reflector = new ReflectionMethod($className, $methodName); + $docComment = $reflector->getDocComment(); + $docComment = \substr($docComment, 3, -2); + + if (\preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $class = $matches[1]; + $code = null; + $message = ''; + $messageRegExp = ''; + + if (isset($matches[2])) { + $message = \trim($matches[2]); + } elseif (isset($annotations['method']['expectedExceptionMessage'])) { + $message = self::parseAnnotationContent( + $annotations['method']['expectedExceptionMessage'][0] + ); + } + + if (isset($annotations['method']['expectedExceptionMessageRegExp'])) { + $messageRegExp = self::parseAnnotationContent( + $annotations['method']['expectedExceptionMessageRegExp'][0] + ); + } + + if (isset($matches[3])) { + $code = $matches[3]; + } elseif (isset($annotations['method']['expectedExceptionCode'])) { + $code = self::parseAnnotationContent( + $annotations['method']['expectedExceptionCode'][0] + ); + } + + if (\is_numeric($code)) { + $code = (int) $code; + } elseif (\is_string($code) && \defined($code)) { + $code = (int) \constant($code); + } + + return [ + 'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp, + ]; + } + + return false; + } + + /** + * Returns the provided data for a method. + * + * @throws Exception + */ + public static function getProvidedData(string $className, string $methodName): ?array + { + $reflector = new ReflectionMethod($className, $methodName); + $docComment = $reflector->getDocComment(); + + $data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName); + + if ($data === null) { + $data = self::getDataFromTestWithAnnotation($docComment); + } + + if ($data === []) { + throw new SkippedTestError; + } + + if ($data !== null) { + foreach ($data as $key => $value) { + if (!\is_array($value)) { + throw new Exception( + \sprintf( + 'Data set %s is invalid.', + \is_int($key) ? '#' . $key : '"' . $key . '"' + ) + ); + } + } + } + + return $data; + } + + /** + * @throws Exception + */ + public static function getDataFromTestWithAnnotation(string $docComment): ?array + { + $docComment = self::cleanUpMultiLineAnnotation($docComment); + + if (\preg_match(self::REGEX_TEST_WITH, $docComment, $matches, \PREG_OFFSET_CAPTURE)) { + $offset = \strlen($matches[0][0]) + $matches[0][1]; + $annotationContent = \substr($docComment, $offset); + $data = []; + + foreach (\explode("\n", $annotationContent) as $candidateRow) { + $candidateRow = \trim($candidateRow); + + if ($candidateRow[0] !== '[') { + break; + } + + $dataSet = \json_decode($candidateRow, true); + + if (\json_last_error() !== \JSON_ERROR_NONE) { + throw new Exception( + 'The data set for the @testWith annotation cannot be parsed: ' . \json_last_error_msg() + ); + } + + $data[] = $dataSet; + } + + if (!$data) { + throw new Exception('The data set for the @testWith annotation cannot be parsed.'); + } + + return $data; + } + + return null; + } + + public static function parseTestMethodAnnotations(string $className, ?string $methodName = ''): array + { + if (!isset(self::$annotationCache[$className])) { + $class = new ReflectionClass($className); + $traits = $class->getTraits(); + $annotations = []; + + foreach ($traits as $trait) { + $annotations = \array_merge( + $annotations, + self::parseAnnotations($trait->getDocComment()) + ); + } + + self::$annotationCache[$className] = \array_merge( + $annotations, + self::parseAnnotations($class->getDocComment()) + ); + } + + $cacheKey = $className . '::' . $methodName; + + if ($methodName !== null && !isset(self::$annotationCache[$cacheKey])) { + try { + $method = new ReflectionMethod($className, $methodName); + $annotations = self::parseAnnotations($method->getDocComment()); + } catch (ReflectionException $e) { + $annotations = []; + } + + self::$annotationCache[$cacheKey] = $annotations; + } + + return [ + 'class' => self::$annotationCache[$className], + 'method' => $methodName !== null ? self::$annotationCache[$cacheKey] : [], + ]; + } + + public static function getInlineAnnotations(string $className, string $methodName): array + { + $method = new ReflectionMethod($className, $methodName); + $code = \file($method->getFileName()); + $lineNumber = $method->getStartLine(); + $startLine = $method->getStartLine() - 1; + $endLine = $method->getEndLine() - 1; + $methodLines = \array_slice($code, $startLine, $endLine - $startLine + 1); + $annotations = []; + + foreach ($methodLines as $line) { + if (\preg_match('#/\*\*?\s*@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?\*/$#m', $line, $matches)) { + $annotations[\strtolower($matches['name'])] = [ + 'line' => $lineNumber, + 'value' => $matches['value'], + ]; + } + + $lineNumber++; + } + + return $annotations; + } + + public static function parseAnnotations(string $docBlock): array + { + $annotations = []; + // Strip away the docblock header and footer to ease parsing of one line annotations + $docBlock = \substr($docBlock, 3, -2); + + if (\preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docBlock, $matches)) { + $numMatches = \count($matches[0]); + + for ($i = 0; $i < $numMatches; ++$i) { + $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i]; + } + } + + return $annotations; + } + + public static function getBackupSettings(string $className, string $methodName): array + { + return [ + 'backupGlobals' => self::getBooleanAnnotationSetting( + $className, + $methodName, + 'backupGlobals' + ), + 'backupStaticAttributes' => self::getBooleanAnnotationSetting( + $className, + $methodName, + 'backupStaticAttributes' + ), + ]; + } + + public static function getDependencies(string $className, string $methodName): array + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $dependencies = []; + + if (isset($annotations['class']['depends'])) { + $dependencies = $annotations['class']['depends']; + } + + if (isset($annotations['method']['depends'])) { + $dependencies = \array_merge( + $dependencies, + $annotations['method']['depends'] + ); + } + + return \array_unique($dependencies); + } + + public static function getErrorHandlerSettings(string $className, ?string $methodName): ?bool + { + return self::getBooleanAnnotationSetting( + $className, + $methodName, + 'errorHandler' + ); + } + + public static function getGroups(string $className, ?string $methodName = ''): array + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $groups = []; + + if (isset($annotations['method']['author'])) { + $groups = $annotations['method']['author']; + } elseif (isset($annotations['class']['author'])) { + $groups = $annotations['class']['author']; + } + + if (isset($annotations['class']['group'])) { + $groups = \array_merge($groups, $annotations['class']['group']); + } + + if (isset($annotations['method']['group'])) { + $groups = \array_merge($groups, $annotations['method']['group']); + } + + if (isset($annotations['class']['ticket'])) { + $groups = \array_merge($groups, $annotations['class']['ticket']); + } + + if (isset($annotations['method']['ticket'])) { + $groups = \array_merge($groups, $annotations['method']['ticket']); + } + + foreach (['method', 'class'] as $element) { + foreach (['small', 'medium', 'large'] as $size) { + if (isset($annotations[$element][$size])) { + $groups[] = $size; + + break 2; + } + } + } + + return \array_unique($groups); + } + + public static function getSize(string $className, ?string $methodName): int + { + $groups = \array_flip(self::getGroups($className, $methodName)); + + if (isset($groups['large'])) { + return self::LARGE; + } + + if (isset($groups['medium'])) { + return self::MEDIUM; + } + + if (isset($groups['small'])) { + return self::SMALL; + } + + return self::UNKNOWN; + } + + public static function getProcessIsolationSettings(string $className, string $methodName): bool + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + return isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess']); + } + + public static function getClassProcessIsolationSettings(string $className, string $methodName): bool + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + return isset($annotations['class']['runClassInSeparateProcess']); + } + + public static function getPreserveGlobalStateSettings(string $className, string $methodName): ?bool + { + return self::getBooleanAnnotationSetting( + $className, + $methodName, + 'preserveGlobalState' + ); + } + + public static function getHookMethods(string $className): array + { + if (!\class_exists($className, false)) { + return self::emptyHookMethodsArray(); + } + + if (!isset(self::$hookMethods[$className])) { + self::$hookMethods[$className] = self::emptyHookMethodsArray(); + + try { + $class = new ReflectionClass($className); + + foreach ($class->getMethods() as $method) { + if ($method->getDeclaringClass()->getName() === Assert::class) { + continue; + } + + if ($method->getDeclaringClass()->getName() === TestCase::class) { + continue; + } + + if ($methodComment = $method->getDocComment()) { + if ($method->isStatic()) { + if (\strpos($methodComment, '@beforeClass') !== false) { + \array_unshift( + self::$hookMethods[$className]['beforeClass'], + $method->getName() + ); + } + + if (\strpos($methodComment, '@afterClass') !== false) { + self::$hookMethods[$className]['afterClass'][] = $method->getName(); + } + } + + if (\preg_match('/@before\b/', $methodComment) > 0) { + \array_unshift( + self::$hookMethods[$className]['before'], + $method->getName() + ); + } + + if (\preg_match('/@after\b/', $methodComment) > 0) { + self::$hookMethods[$className]['after'][] = $method->getName(); + } + } + } + } catch (ReflectionException $e) { + } + } + + return self::$hookMethods[$className]; + } + + /** + * @throws CodeCoverageException + */ + private static function getLinesToBeCoveredOrUsed(string $className, string $methodName, string $mode): array + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + $classShortcut = null; + + if (!empty($annotations['class'][$mode . 'DefaultClass'])) { + if (\count($annotations['class'][$mode . 'DefaultClass']) > 1) { + throw new CodeCoverageException( + \sprintf( + 'More than one @%sClass annotation in class or interface "%s".', + $mode, + $className + ) + ); + } + + $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0]; + } + + $list = []; + + if (isset($annotations['class'][$mode])) { + $list = $annotations['class'][$mode]; + } + + if (isset($annotations['method'][$mode])) { + $list = \array_merge($list, $annotations['method'][$mode]); + } + + $codeList = []; + + foreach (\array_unique($list) as $element) { + if ($classShortcut && \strncmp($element, '::', 2) === 0) { + $element = $classShortcut . $element; + } + + $element = \preg_replace('/[\s()]+$/', '', $element); + $element = \explode(' ', $element); + $element = $element[0]; + + if ($mode === 'covers' && \interface_exists($element)) { + throw new InvalidCoversTargetException( + \sprintf( + 'Trying to @cover interface "%s".', + $element + ) + ); + } + + $codeList = \array_merge( + $codeList, + self::resolveElementToReflectionObjects($element) + ); + } + + return self::resolveReflectionObjectsToLines($codeList); + } + + /** + * Parse annotation content to use constant/class constant values + * + * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME + * + * If the constant is not found the string is used as is to ensure maximum BC. + */ + private static function parseAnnotationContent(string $message): string + { + if (\defined($message) && (\strpos($message, '::') !== false && \substr_count($message, '::') + 1 === 2)) { + $message = \constant($message); + } + + return $message; + } + + /** + * Returns the provided data for a method. + */ + private static function getDataFromDataProviderAnnotation(string $docComment, string $className, string $methodName): ?iterable + { + if (\preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { + $result = []; + + foreach ($matches[1] as $match) { + $dataProviderMethodNameNamespace = \explode('\\', $match); + $leaf = \explode('::', \array_pop($dataProviderMethodNameNamespace)); + $dataProviderMethodName = \array_pop($leaf); + + if (empty($dataProviderMethodNameNamespace)) { + $dataProviderMethodNameNamespace = ''; + } else { + $dataProviderMethodNameNamespace = \implode('\\', $dataProviderMethodNameNamespace) . '\\'; + } + + if (empty($leaf)) { + $dataProviderClassName = $className; + } else { + $dataProviderClassName = $dataProviderMethodNameNamespace . \array_pop($leaf); + } + + $dataProviderClass = new ReflectionClass($dataProviderClassName); + $dataProviderMethod = $dataProviderClass->getMethod( + $dataProviderMethodName + ); + + if ($dataProviderMethod->isStatic()) { + $object = null; + } else { + $object = $dataProviderClass->newInstance(); + } + + if ($dataProviderMethod->getNumberOfParameters() === 0) { + $data = $dataProviderMethod->invoke($object); + } else { + $data = $dataProviderMethod->invoke($object, $methodName); + } + + if ($data instanceof Traversable) { + $origData = $data; + $data = []; + + foreach ($origData as $key => $value) { + if (\is_int($key)) { + $data[] = $value; + } else { + $data[$key] = $value; + } + } + } + + if (\is_array($data)) { + $result = \array_merge($result, $data); + } + } + + return $result; + } + + return null; + } + + private static function cleanUpMultiLineAnnotation(string $docComment): string + { + //removing initial ' * ' for docComment + $docComment = \str_replace("\r\n", "\n", $docComment); + $docComment = \preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment); + $docComment = \substr($docComment, 0, -1); + + return \rtrim($docComment, "\n"); + } + + private static function emptyHookMethodsArray(): array + { + return [ + 'beforeClass' => ['setUpBeforeClass'], + 'before' => ['setUp'], + 'after' => ['tearDown'], + 'afterClass' => ['tearDownAfterClass'], + ]; + } + + private static function getBooleanAnnotationSetting(string $className, ?string $methodName, string $settingName): ?bool + { + $annotations = self::parseTestMethodAnnotations( + $className, + $methodName + ); + + if (isset($annotations['method'][$settingName])) { + if ($annotations['method'][$settingName][0] === 'enabled') { + return true; + } + + if ($annotations['method'][$settingName][0] === 'disabled') { + return false; + } + } + + if (isset($annotations['class'][$settingName])) { + if ($annotations['class'][$settingName][0] === 'enabled') { + return true; + } + + if ($annotations['class'][$settingName][0] === 'disabled') { + return false; + } + } + + return null; + } + + /** + * @throws InvalidCoversTargetException + */ + private static function resolveElementToReflectionObjects(string $element): array + { + $codeToCoverList = []; + + if (\strpos($element, '\\') !== false && \function_exists($element)) { + $codeToCoverList[] = new ReflectionFunction($element); + } elseif (\strpos($element, '::') !== false) { + [$className, $methodName] = \explode('::', $element); + + if (isset($methodName[0]) && $methodName[0] === '<') { + $classes = [$className]; + + foreach ($classes as $className) { + if (!\class_exists($className) && + !\interface_exists($className) && + !\trait_exists($className)) { + throw new InvalidCoversTargetException( + \sprintf( + 'Trying to @cover or @use not existing class or ' . + 'interface "%s".', + $className + ) + ); + } + + $class = new ReflectionClass($className); + $methods = $class->getMethods(); + $inverse = isset($methodName[1]) && $methodName[1] === '!'; + $visibility = 'isPublic'; + + if (\strpos($methodName, 'protected')) { + $visibility = 'isProtected'; + } elseif (\strpos($methodName, 'private')) { + $visibility = 'isPrivate'; + } + + foreach ($methods as $method) { + if ($inverse && !$method->$visibility()) { + $codeToCoverList[] = $method; + } elseif (!$inverse && $method->$visibility()) { + $codeToCoverList[] = $method; + } + } + } + } else { + $classes = [$className]; + + foreach ($classes as $className) { + if ($className === '' && \function_exists($methodName)) { + $codeToCoverList[] = new ReflectionFunction( + $methodName + ); + } else { + if (!((\class_exists($className) || \interface_exists($className) || \trait_exists($className)) && + \method_exists($className, $methodName))) { + throw new InvalidCoversTargetException( + \sprintf( + 'Trying to @cover or @use not existing method "%s::%s".', + $className, + $methodName + ) + ); + } + + $codeToCoverList[] = new ReflectionMethod( + $className, + $methodName + ); + } + } + } + } else { + $extended = false; + + if (\strpos($element, '') !== false) { + $element = \str_replace('', '', $element); + $extended = true; + } + + $classes = [$element]; + + if ($extended) { + $classes = \array_merge( + $classes, + \class_implements($element), + \class_parents($element) + ); + } + + foreach ($classes as $className) { + if (!\class_exists($className) && + !\interface_exists($className) && + !\trait_exists($className)) { + throw new InvalidCoversTargetException( + \sprintf( + 'Trying to @cover or @use not existing class or ' . + 'interface "%s".', + $className + ) + ); + } + + $codeToCoverList[] = new ReflectionClass($className); + } + } + + return $codeToCoverList; + } + + private static function resolveReflectionObjectsToLines(array $reflectors): array + { + $result = []; + + foreach ($reflectors as $reflector) { + if ($reflector instanceof ReflectionClass) { + foreach ($reflector->getTraits() as $trait) { + $reflectors[] = $trait; + } + } + } + + foreach ($reflectors as $reflector) { + $filename = $reflector->getFileName(); + + if (!isset($result[$filename])) { + $result[$filename] = []; + } + + $result[$filename] = \array_merge( + $result[$filename], + \range($reflector->getStartLine(), $reflector->getEndLine()) + ); + } + + foreach ($result as $filename => $lineNumbers) { + $result[$filename] = \array_keys(\array_flip($lineNumbers)); + } + + return $result; + } + + /** + * Trims any extensions from version string that follows after + * the .[.] format + */ + private static function sanitizeVersionNumber(string $version) + { + return \preg_replace( + '/^(\d+\.\d+(?:.\d+)?).*$/', + '$1', + $version + ); + } + + private static function shouldCoversAnnotationBeUsed(array $annotations): bool + { + if (isset($annotations['method']['coversNothing'])) { + return false; + } + + if (isset($annotations['method']['covers'])) { + return true; + } + + if (isset($annotations['class']['coversNothing'])) { + return false; + } + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Error\Deprecated; +use PHPUnit\Framework\Error\Error; +use PHPUnit\Framework\Error\Notice; +use PHPUnit\Framework\Error\Warning; + +/** + * Error handler that converts PHP errors and warnings to exceptions. + */ +final class ErrorHandler +{ + private static $errorStack = []; + + /** + * Returns the error stack. + */ + public static function getErrorStack(): array + { + return self::$errorStack; + } + + public static function handleError(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool + { + if (!($errorNumber & \error_reporting())) { + return false; + } + + self::$errorStack[] = [$errorNumber, $errorString, $errorFile, $errorLine]; + + $trace = \debug_backtrace(); + \array_shift($trace); + + foreach ($trace as $frame) { + if ($frame['function'] === '__toString') { + return false; + } + } + + if ($errorNumber === \E_NOTICE || $errorNumber === \E_USER_NOTICE || $errorNumber === \E_STRICT) { + if (Notice::$enabled !== true) { + return false; + } + + $exception = Notice::class; + } elseif ($errorNumber === \E_WARNING || $errorNumber === \E_USER_WARNING) { + if (Warning::$enabled !== true) { + return false; + } + + $exception = Warning::class; + } elseif ($errorNumber === \E_DEPRECATED || $errorNumber === \E_USER_DEPRECATED) { + if (Deprecated::$enabled !== true) { + return false; + } + + $exception = Deprecated::class; + } else { + $exception = Error::class; + } + + throw new $exception($errorString, $errorNumber, $errorFile, $errorLine); + } + + /** + * Registers an error handler and returns a function that will restore + * the previous handler when invoked + * + * @param int $severity PHP predefined error constant + * + * @throws \Exception if event of specified severity is emitted + */ + public static function handleErrorOnce($severity = \E_WARNING): callable + { + $terminator = function () { + static $expired = false; + + if (!$expired) { + $expired = true; + + return \restore_error_handler(); + } + }; + + \set_error_handler( + function ($errorNumber, $errorString) use ($severity) { + if ($errorNumber === $severity) { + return; + } + + return false; + } + ); + + return $terminator; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\SyntheticError; + +final class Filter +{ + public static function getFilteredStacktrace(\Throwable $t): string + { + $prefix = false; + $script = \realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); + + if (\defined('__PHPUNIT_PHAR_ROOT__')) { + $prefix = __PHPUNIT_PHAR_ROOT__; + } + + $filteredStacktrace = ''; + + if ($t instanceof SyntheticError) { + $eTrace = $t->getSyntheticTrace(); + $eFile = $t->getSyntheticFile(); + $eLine = $t->getSyntheticLine(); + } elseif ($t instanceof Exception) { + $eTrace = $t->getSerializableTrace(); + $eFile = $t->getFile(); + $eLine = $t->getLine(); + } else { + if ($t->getPrevious()) { + $t = $t->getPrevious(); + } + + $eTrace = $t->getTrace(); + $eFile = $t->getFile(); + $eLine = $t->getLine(); + } + + if (!self::frameExists($eTrace, $eFile, $eLine)) { + \array_unshift( + $eTrace, + ['file' => $eFile, 'line' => $eLine] + ); + } + + $blacklist = new Blacklist; + + foreach ($eTrace as $frame) { + if (isset($frame['file']) && \is_file($frame['file']) && + (empty($GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST']) || !\in_array($frame['file'], $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'])) && + !$blacklist->isBlacklisted($frame['file']) && + ($prefix === false || \strpos($frame['file'], $prefix) !== 0) && + $frame['file'] !== $script) { + $filteredStacktrace .= \sprintf( + "%s:%s\n", + $frame['file'], + $frame['line'] ?? '?' + ); + } + } + + return $filteredStacktrace; + } + + private static function frameExists(array $trace, string $file, int $line): bool + { + foreach ($trace as $frame) { + if (isset($frame['file']) && $frame['file'] === $file && + isset($frame['line']) && $frame['line'] === $line) { + return true; + } + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +/** + * Prints TestDox documentation in HTML format. + */ +final class HtmlResultPrinter extends ResultPrinter +{ + /** + * @var string + */ + private const PAGE_HEADER = << + + + + Test Documentation + + + +EOT; + + /** + * @var string + */ + private const CLASS_HEADER = <<%s +
    + +EOT; + + /** + * @var string + */ + private const CLASS_FOOTER = << +EOT; + + /** + * @var string + */ + private const PAGE_FOOTER = << + +EOT; + + /** + * Handler for 'start run' event. + */ + protected function startRun(): void + { + $this->write(self::PAGE_HEADER); + } + + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name): void + { + $this->write( + \sprintf( + self::CLASS_HEADER, + $name, + $this->currentTestClassPrettified + ) + ); + } + + /** + * Handler for 'on test' event. + */ + protected function onTest($name, bool $success = true): void + { + $this->write( + \sprintf( + "
  • %s %s
  • \n", + $success ? '#555753' : '#ef2929', + $success ? '✓' : '❌', + $name + ) + ); + } + + /** + * Handler for 'end class' event. + */ + protected function endClass(string $name): void + { + $this->write(self::CLASS_FOOTER); + } + + /** + * Handler for 'end run' event. + */ + protected function endRun(): void + { + $this->write(self::PAGE_FOOTER); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use PHPUnit\Framework\TestCase; +use SebastianBergmann\Exporter\Exporter; + +/** + * Prettifies class and method names for use in TestDox documentation. + */ +final class NamePrettifier +{ + /** + * @var array + */ + private $strings = []; + + /** + * Prettifies the name of a test class. + */ + public function prettifyTestClass(string $className): string + { + try { + $annotations = \PHPUnit\Util\Test::parseTestMethodAnnotations($className); + + if (isset($annotations['class']['testdox'][0])) { + return $annotations['class']['testdox'][0]; + } + } catch (\ReflectionException $e) { + } + + $result = $className; + + if (\substr($className, -1 * \strlen('Test')) === 'Test') { + $result = \substr($result, 0, \strripos($result, 'Test')); + } + + if (\strpos($className, 'Tests') === 0) { + $result = \substr($result, \strlen('Tests')); + } elseif (\strpos($className, 'Test') === 0) { + $result = \substr($result, \strlen('Test')); + } + + if ($result[0] === '\\') { + $result = \substr($result, 1); + } + + return $result; + } + + /** + * @throws \ReflectionException + */ + public function prettifyTestCase(TestCase $test): string + { + $annotations = $test->getAnnotations(); + $annotationWithPlaceholders = false; + + $callback = static function (string $variable): string { + return \sprintf('/%s(?=\b)/', \preg_quote($variable, '/')); + }; + + if (isset($annotations['method']['testdox'][0])) { + $result = $annotations['method']['testdox'][0]; + + if (\strpos($result, '$') !== false) { + $annotation = $annotations['method']['testdox'][0]; + $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test); + $variables = \array_map($callback, \array_keys($providedData)); + + $result = \trim(\preg_replace($variables, $providedData, $annotation)); + + $annotationWithPlaceholders = true; + } + } else { + $result = $this->prettifyTestMethod($test->getName(false)); + } + + if ($test->usesDataProvider() && !$annotationWithPlaceholders) { + $result .= $test->getDataSetAsString(false); + } + + return $result; + } + + /** + * Prettifies the name of a test method. + */ + public function prettifyTestMethod(string $name): string + { + $buffer = ''; + + if (!\is_string($name) || $name === '') { + return $buffer; + } + + $string = \preg_replace('#\d+$#', '', $name, -1, $count); + + if (\in_array($string, $this->strings)) { + $name = $string; + } elseif ($count === 0) { + $this->strings[] = $string; + } + + if (\strpos($name, 'test_') === 0) { + $name = \substr($name, 5); + } elseif (\strpos($name, 'test') === 0) { + $name = \substr($name, 4); + } + + if ($name === '') { + return $buffer; + } + + $name[0] = \strtoupper($name[0]); + + if (\strpos($name, '_') !== false) { + return \trim(\str_replace('_', ' ', $name)); + } + + $max = \strlen($name); + $wasNumeric = false; + + for ($i = 0; $i < $max; $i++) { + if ($i > 0 && \ord($name[$i]) >= 65 && \ord($name[$i]) <= 90) { + $buffer .= ' ' . \strtolower($name[$i]); + } else { + $isNumeric = \is_numeric($name[$i]); + + if (!$wasNumeric && $isNumeric) { + $buffer .= ' '; + $wasNumeric = true; + } + + if ($wasNumeric && !$isNumeric) { + $wasNumeric = false; + } + + $buffer .= $name[$i]; + } + } + + return $buffer; + } + + /** + * @throws \ReflectionException + */ + private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test): array + { + $reflector = new \ReflectionMethod(\get_class($test), $test->getName(false)); + $providedData = []; + $providedDataValues = \array_values($test->getProvidedData()); + $i = 0; + + foreach ($reflector->getParameters() as $parameter) { + if (!\array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) { + $providedDataValues[$i] = $parameter->getDefaultValue(); + } + + $value = $providedDataValues[$i++] ?? null; + + if (\is_object($value)) { + $reflector = new \ReflectionObject($value); + + if ($reflector->hasMethod('__toString')) { + $value = (string) $value; + } + } + + if (!\is_scalar($value)) { + $value = \gettype($value); + } + + if (\is_bool($value) || \is_int($value) || \is_float($value)) { + $exporter = new Exporter; + + $value = $exporter->export($value); + } + + $providedData['$' . $parameter->getName()] = $value; + } + + return $providedData; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +/** + * Prints TestDox documentation in text format to files. + * For the CLI testdox printer please refer to \PHPUnit\TextUI\TextDoxPrinter. + */ +class TextResultPrinter extends ResultPrinter +{ + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name): void + { + $this->write($this->currentTestClassPrettified . "\n"); + } + + /** + * Handler for 'on test' event. + */ + protected function onTest($name, bool $success = true): void + { + if ($success) { + $this->write(' [x] '); + } else { + $this->write(' [ ] '); + } + + $this->write($name . "\n"); + } + + /** + * Handler for 'end class' event. + */ + protected function endClass(string $name): void + { + $this->write("\n"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TextUI\ResultPrinter; +use SebastianBergmann\Timer\Timer; + +/** + * This printer is for CLI output only. For the classes that output to file, html and xml, + * please refer to the PHPUnit\Util\TestDox namespace + */ +class CliTestDoxPrinter extends ResultPrinter +{ + /** + * @var int[] + */ + private $nonSuccessfulTestResults = []; + + /** + * @var NamePrettifier + */ + private $prettifier; + + /** + * @var int The number of test results received from the TestRunner + */ + private $testIndex = 0; + + /** + * @var int The number of test results already sent to the output + */ + private $testFlushIndex = 0; + + /** + * @var array Buffer for write() + */ + private $outputBuffer = []; + + /** + * @var bool + */ + private $bufferExecutionOrder = false; + + /** + * @var array array + */ + private $originalExecutionOrder = []; + + /** + * @var string Classname of the current test + */ + private $className = ''; + + /** + * @var string Classname of the previous test; empty for first test + */ + private $lastClassName = ''; + + /** + * @var string Prettified test name of current test + */ + private $testMethod; + + /** + * @var string Test result message of current test + */ + private $testResultMessage; + + /** + * @var bool Test result message of current test contains a verbose dump + */ + private $lastFlushedTestWasVerbose = false; + + public function __construct( + $out = null, + bool $verbose = false, + $colors = self::COLOR_DEFAULT, + bool $debug = false, + $numberOfColumns = 80, + bool $reverse = false + ) { + parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); + + $this->prettifier = new NamePrettifier; + } + + public function setOriginalExecutionOrder(array $order): void + { + $this->originalExecutionOrder = $order; + $this->bufferExecutionOrder = !empty($order); + } + + public function startTest(Test $test): void + { + if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) { + return; + } + + $this->lastTestFailed = false; + $this->lastClassName = $this->className; + $this->testResultMessage = ''; + + if ($test instanceof TestCase) { + $className = $this->prettifier->prettifyTestClass(\get_class($test)); + $testMethod = $this->prettifier->prettifyTestCase($test); + } elseif ($test instanceof PhptTestCase) { + $className = \get_class($test); + $testMethod = $test->getName(); + } + + $this->className = $className; + $this->testMethod = $testMethod; + + parent::startTest($test); + } + + public function endTest(Test $test, float $time): void + { + if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) { + return; + } + + if ($test instanceof TestCase || $test instanceof PhptTestCase) { + $this->testIndex++; + } + + if ($this->lastTestFailed) { + $resultMessage = $this->testResultMessage; + $this->nonSuccessfulTestResults[] = $this->testIndex; + } else { + $resultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-green', '✔'), + '', + $time, + $this->verbose + ); + } + + if ($this->bufferExecutionOrder) { + $this->bufferTestResult($test, $resultMessage); + $this->flushOutputBuffer(); + } else { + $this->writeTestResult($resultMessage); + + if ($this->lastTestFailed) { + $this->bufferTestResult($test, $resultMessage); + } + } + + parent::endTest($test, $time); + } + + public function addError(Test $test, \Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->testResultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-yellow', '✘'), + (string) $t, + $time, + true + ); + } + + public function addWarning(Test $test, Warning $e, float $time): void + { + $this->lastTestFailed = true; + $this->testResultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-yellow', '✘'), + (string) $e, + $time, + true + ); + } + + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $this->lastTestFailed = true; + $this->testResultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-red', '✘'), + (string) $e, + $time, + true + ); + } + + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->testResultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-yellow', '∅'), + (string) $t, + $time, + false + ); + } + + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->testResultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-yellow', '☢'), + (string) $t, + $time, + false + ); + } + + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->testResultMessage = $this->formatTestResultMessage( + $this->formatWithColor('fg-yellow', '→'), + (string) $t, + $time, + false + ); + } + + public function bufferTestResult(Test $test, string $msg): void + { + $this->outputBuffer[$this->testIndex] = [ + 'className' => $this->className, + 'testName' => TestSuiteSorter::getTestSorterUID($test), + 'testMethod' => $this->testMethod, + 'message' => $msg, + 'failed' => $this->lastTestFailed, + 'verbose' => $this->lastFlushedTestWasVerbose, + ]; + } + + public function writeTestResult(string $msg): void + { + $msg = $this->formatTestSuiteHeader($this->lastClassName, $this->className, $msg); + $this->write($msg); + } + + public function writeProgress(string $progress): void + { + } + + public function flush(): void + { + } + + public function printResult(TestResult $result): void + { + $this->printHeader(); + + $this->printNonSuccessfulTestsSummary($result->count()); + + $this->printFooter($result); + } + + protected function printHeader(): void + { + $this->write("\n" . Timer::resourceUsage() . "\n\n"); + } + + private function flushOutputBuffer(): void + { + if ($this->testFlushIndex === $this->testIndex) { + return; + } + + if ($this->testFlushIndex > 0) { + $prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]); + } else { + $prevResult = $this->getEmptyTestResult(); + } + + do { + $flushed = false; + $result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]); + + if (!empty($result)) { + $this->writeBufferTestResult($prevResult, $result); + $this->testFlushIndex++; + $prevResult = $result; + $flushed = true; + } + } while ($flushed && $this->testFlushIndex < $this->testIndex); + } + + private function writeBufferTestResult(array $prevResult, array $result): void + { + // Write spacer line for new suite headers and after verbose messages + if ($prevResult['testName'] !== '' && + ($prevResult['verbose'] === true || $prevResult['className'] !== $result['className'])) { + $this->write("\n"); + } + + // Write suite header + if ($prevResult['className'] !== $result['className']) { + $this->write($result['className'] . "\n"); + } + + // Write the test result itself + $this->write($result['message']); + } + + private function getTestResultByName(string $testName): array + { + foreach ($this->outputBuffer as $result) { + if ($result['testName'] === $testName) { + return $result; + } + } + + return []; + } + + private function formatTestSuiteHeader(?string $lastClassName, string $className, string $msg): string + { + if ($lastClassName === null || $className !== $lastClassName) { + return \sprintf( + "%s%s\n%s", + ($this->lastClassName !== '') ? "\n" : '', + $className, + $msg + ); + } + + return $msg; + } + + private function formatTestResultMessage( + string $symbol, + string $resultMessage, + float $time, + bool $alwaysVerbose = false + ): string { + $additionalInformation = $this->getFormattedAdditionalInformation($resultMessage, $alwaysVerbose); + $msg = \sprintf( + " %s %s%s\n%s", + $symbol, + $this->testMethod, + $this->verbose ? ' ' . $this->getFormattedRuntime($time) : '', + $additionalInformation + ); + + $this->lastFlushedTestWasVerbose = !empty($additionalInformation); + + return $msg; + } + + private function getFormattedRuntime(float $time): string + { + if ($time > 5) { + return $this->formatWithColor('fg-red', \sprintf('[%.2f ms]', $time * 1000)); + } + + if ($time > 1) { + return $this->formatWithColor('fg-yellow', \sprintf('[%.2f ms]', $time * 1000)); + } + + return \sprintf('[%.2f ms]', $time * 1000); + } + + private function getFormattedAdditionalInformation(string $resultMessage, bool $verbose): string + { + if ($resultMessage === '') { + return ''; + } + + if (!($this->verbose || $verbose)) { + return ''; + } + + return \sprintf( + " │\n%s\n", + \implode( + "\n", + \array_map( + function (string $text) { + return \sprintf(' │ %s', $text); + }, + \explode("\n", $resultMessage) + ) + ) + ); + } + + private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests): void + { + if (empty($this->nonSuccessfulTestResults)) { + return; + } + + if ((\count($this->nonSuccessfulTestResults) / $numberOfExecutedTests) >= 0.7) { + return; + } + + $this->write("Summary of non-successful tests:\n\n"); + + $prevResult = $this->getEmptyTestResult(); + + foreach ($this->nonSuccessfulTestResults as $testIndex) { + $result = $this->outputBuffer[$testIndex]; + $this->writeBufferTestResult($prevResult, $result); + $prevResult = $result; + } + } + + private function getEmptyTestResult(): array + { + return [ + 'className' => '', + 'testName' => '', + 'message' => '', + 'failed' => '', + 'verbose' => '', + ]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +final class TestResult +{ + /** + * @var callable + */ + private $colorize; + + /** + * @var string + */ + private $testClass; + + /** + * @var string + */ + private $testMethod; + + /** + * @var bool + */ + private $testSuccesful; + + /** + * @var string + */ + private $symbol; + + /** + * @var string + */ + private $additionalInformation; + + /** + * @var bool + */ + private $additionalInformationVerbose; + + /** + * @var float + */ + private $runtime; + + public function __construct(callable $colorize, string $testClass, string $testMethod) + { + $this->colorize = $colorize; + $this->testClass = $testClass; + $this->testMethod = $testMethod; + $this->testSuccesful = true; + $this->symbol = ($this->colorize)('fg-green', '✔'); + $this->additionalInformation = ''; + } + + public function isTestSuccessful(): bool + { + return $this->testSuccesful; + } + + public function fail(string $symbol, string $additionalInformation, bool $additionalInformationVerbose = false): void + { + $this->testSuccesful = false; + $this->symbol = $symbol; + $this->additionalInformation = $additionalInformation; + $this->additionalInformationVerbose = $additionalInformationVerbose; + } + + public function setRuntime(float $runtime): void + { + $this->runtime = $runtime; + } + + public function toString(?self $previousTestResult, $verbose = false): string + { + return \sprintf( + "%s%s %s %s%s\n%s", + $previousTestResult && $previousTestResult->additionalInformationPrintable($verbose) ? "\n" : '', + $this->getClassNameHeader($previousTestResult ? $previousTestResult->testClass : null), + $this->symbol, + $this->testMethod, + $verbose ? ' ' . $this->getFormattedRuntime() : '', + $this->getFormattedAdditionalInformation($verbose) + ); + } + + private function getClassNameHeader(?string $previousTestClass): string + { + $className = ''; + + if ($this->testClass !== $previousTestClass) { + if (null !== $previousTestClass) { + $className = "\n"; + } + + $className .= \sprintf("%s\n", $this->testClass); + } + + return $className; + } + + private function getFormattedRuntime(): string + { + if ($this->runtime > 5) { + return ($this->colorize)('fg-red', \sprintf('[%.2f ms]', $this->runtime * 1000)); + } + + if ($this->runtime > 1) { + return ($this->colorize)('fg-yellow', \sprintf('[%.2f ms]', $this->runtime * 1000)); + } + + return \sprintf('[%.2f ms]', $this->runtime * 1000); + } + + private function getFormattedAdditionalInformation($verbose): string + { + if (!$this->additionalInformationPrintable($verbose)) { + return ''; + } + + return \sprintf( + " │\n%s\n", + \implode( + "\n", + \array_map( + function (string $text) { + return \sprintf(' │ %s', $text); + }, + \explode("\n", $this->additionalInformation) + ) + ) + ); + } + + private function additionalInformationPrintable(bool $verbose): bool + { + if ($this->additionalInformation === '') { + return false; + } + + if ($this->additionalInformationVerbose && !$verbose) { + return false; + } + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use DOMDocument; +use DOMElement; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Util\Printer; +use ReflectionClass; + +class XmlResultPrinter extends Printer implements TestListener +{ + /** + * @var DOMDocument + */ + private $document; + + /** + * @var DOMElement + */ + private $root; + + /** + * @var NamePrettifier + */ + private $prettifier; + + /** + * @var null|\Throwable + */ + private $exception; + + /** + * @param resource|string $out + * + * @throws Exception + */ + public function __construct($out = null) + { + $this->document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + + $this->root = $this->document->createElement('tests'); + $this->document->appendChild($this->root); + + $this->prettifier = new NamePrettifier; + + parent::__construct($out); + } + + /** + * Flush buffer and close output. + */ + public function flush(): void + { + $this->write($this->document->saveXML()); + + parent::flush(); + } + + /** + * An error occurred. + */ + public function addError(Test $test, \Throwable $t, float $time): void + { + $this->exception = $t; + } + + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + } + + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $this->exception = $e; + } + + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + } + + /** + * Risky test. + */ + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + } + + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + } + + /** + * A test suite started. + */ + public function startTestSuite(TestSuite $suite): void + { + } + + /** + * A test suite ended. + */ + public function endTestSuite(TestSuite $suite): void + { + } + + /** + * A test started. + */ + public function startTest(Test $test): void + { + $this->exception = null; + } + + /** + * A test ended. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function endTest(Test $test, float $time): void + { + if (!$test instanceof TestCase) { + return; + } + + /* @var TestCase $test */ + + $groups = \array_filter( + $test->getGroups(), + function ($group) { + return !($group === 'small' || $group === 'medium' || $group === 'large'); + } + ); + + $node = $this->document->createElement('test'); + + $node->setAttribute('className', \get_class($test)); + $node->setAttribute('methodName', $test->getName()); + $node->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(\get_class($test))); + $node->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test)); + $node->setAttribute('status', $test->getStatus()); + $node->setAttribute('time', $time); + $node->setAttribute('size', $test->getSize()); + $node->setAttribute('groups', \implode(',', $groups)); + + $inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(\get_class($test), $test->getName()); + + if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) { + $node->setAttribute('given', $inlineAnnotations['given']['value']); + $node->setAttribute('givenStartLine', $inlineAnnotations['given']['line']); + $node->setAttribute('when', $inlineAnnotations['when']['value']); + $node->setAttribute('whenStartLine', $inlineAnnotations['when']['line']); + $node->setAttribute('then', $inlineAnnotations['then']['value']); + $node->setAttribute('thenStartLine', $inlineAnnotations['then']['line']); + } + + if ($this->exception !== null) { + if ($this->exception instanceof Exception) { + $steps = $this->exception->getSerializableTrace(); + } else { + $steps = $this->exception->getTrace(); + } + + $class = new ReflectionClass($test); + $file = $class->getFileName(); + + foreach ($steps as $step) { + if (isset($step['file']) && $step['file'] === $file) { + $node->setAttribute('exceptionLine', $step['line']); + + break; + } + } + + $node->setAttribute('exceptionMessage', $this->exception->getMessage()); + } + + $this->root->appendChild($node); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\TestDox; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Framework\WarningTestCase; +use PHPUnit\Runner\BaseTestRunner; +use PHPUnit\Util\Printer; + +/** + * Base class for printers of TestDox documentation. + */ +abstract class ResultPrinter extends Printer implements TestListener +{ + /** + * @var NamePrettifier + */ + protected $prettifier; + + /** + * @var string + */ + protected $testClass = ''; + + /** + * @var int + */ + protected $testStatus; + + /** + * @var array + */ + protected $tests = []; + + /** + * @var int + */ + protected $successful = 0; + + /** + * @var int + */ + protected $warned = 0; + + /** + * @var int + */ + protected $failed = 0; + + /** + * @var int + */ + protected $risky = 0; + + /** + * @var int + */ + protected $skipped = 0; + + /** + * @var int + */ + protected $incomplete = 0; + + /** + * @var null|string + */ + protected $currentTestClassPrettified; + + /** + * @var null|string + */ + protected $currentTestMethodPrettified; + + /** + * @var array + */ + private $groups; + + /** + * @var array + */ + private $excludeGroups; + + /** + * @param resource $out + * + * @throws \PHPUnit\Framework\Exception + */ + public function __construct($out = null, array $groups = [], array $excludeGroups = []) + { + parent::__construct($out); + + $this->groups = $groups; + $this->excludeGroups = $excludeGroups; + + $this->prettifier = new NamePrettifier; + $this->startRun(); + } + + /** + * Flush buffer and close output. + */ + public function flush(): void + { + $this->doEndClass(); + $this->endRun(); + + parent::flush(); + } + + /** + * An error occurred. + */ + public function addError(Test $test, \Throwable $t, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = BaseTestRunner::STATUS_ERROR; + $this->failed++; + } + + /** + * A warning occurred. + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = BaseTestRunner::STATUS_WARNING; + $this->warned++; + } + + /** + * A failure occurred. + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = BaseTestRunner::STATUS_FAILURE; + $this->failed++; + } + + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = BaseTestRunner::STATUS_INCOMPLETE; + $this->incomplete++; + } + + /** + * Risky test. + */ + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = BaseTestRunner::STATUS_RISKY; + $this->risky++; + } + + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->testStatus = BaseTestRunner::STATUS_SKIPPED; + $this->skipped++; + } + + /** + * A testsuite started. + */ + public function startTestSuite(TestSuite $suite): void + { + } + + /** + * A testsuite ended. + */ + public function endTestSuite(TestSuite $suite): void + { + } + + /** + * A test started. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + */ + public function startTest(Test $test): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $class = \get_class($test); + + if ($this->testClass !== $class) { + if ($this->testClass !== '') { + $this->doEndClass(); + } + + $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); + $this->testClass = $class; + $this->tests = []; + + $this->startClass($class); + } + + if ($test instanceof TestCase) { + $this->currentTestMethodPrettified = $this->prettifier->prettifyTestCase($test); + } + + $this->testStatus = BaseTestRunner::STATUS_PASSED; + } + + /** + * A test ended. + */ + public function endTest(Test $test, float $time): void + { + if (!$this->isOfInterest($test)) { + return; + } + + $this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus]; + + $this->currentTestClassPrettified = null; + $this->currentTestMethodPrettified = null; + } + + protected function doEndClass(): void + { + foreach ($this->tests as $test) { + $this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED); + } + + $this->endClass($this->testClass); + } + + /** + * Handler for 'start run' event. + */ + protected function startRun(): void + { + } + + /** + * Handler for 'start class' event. + */ + protected function startClass(string $name): void + { + } + + /** + * Handler for 'on test' event. + */ + protected function onTest($name, bool $success = true): void + { + } + + /** + * Handler for 'end class' event. + */ + protected function endClass(string $name): void + { + } + + /** + * Handler for 'end run' event. + */ + protected function endRun(): void + { + } + + private function isOfInterest(Test $test): bool + { + if (!$test instanceof TestCase) { + return false; + } + + if ($test instanceof WarningTestCase) { + return false; + } + + if (!empty($this->groups)) { + foreach ($test->getGroups() as $group) { + if (\in_array($group, $this->groups)) { + return true; + } + } + + return false; + } + + if (!empty($this->excludeGroups)) { + foreach ($test->getGroups() as $group) { + if (\in_array($group, $this->excludeGroups)) { + return false; + } + } + + return true; + } + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Exception; + +/** + * Factory for PHPUnit\Framework\Exception objects that are used to describe + * invalid arguments passed to a function or method. + */ +final class InvalidArgumentHelper +{ + public static function factory(int $argument, string $type, $value = null): Exception + { + $stack = \debug_backtrace(); + + return new Exception( + \sprintf( + 'Argument #%d%sof %s::%s() must be a %s', + $argument, + $value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ', + $stack[1]['class'], + $stack[1]['function'], + $type + ) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; + +final class XmlTestListRenderer +{ + public function render(TestSuite $suite): string + { + $writer = new \XMLWriter; + + $writer->openMemory(); + $writer->setIndent(true); + $writer->startDocument(); + $writer->startElement('tests'); + + $currentTestCase = null; + + foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) { + if ($test instanceof TestCase) { + if (\get_class($test) !== $currentTestCase) { + if ($currentTestCase !== null) { + $writer->endElement(); + } + + $writer->startElement('testCaseClass'); + $writer->writeAttribute('name', \get_class($test)); + + $currentTestCase = \get_class($test); + } + + $writer->startElement('testCaseMethod'); + $writer->writeAttribute('name', $test->getName(false)); + $writer->writeAttribute('groups', \implode(',', $test->getGroups())); + + if (!empty($test->getDataSetAsString(false))) { + $writer->writeAttribute( + 'dataSet', + \str_replace( + ' with data set ', + '', + $test->getDataSetAsString(false) + ) + ); + } + + $writer->endElement(); + } elseif ($test instanceof PhptTestCase) { + if ($currentTestCase !== null) { + $writer->endElement(); + + $currentTestCase = null; + } + + $writer->startElement('phptFile'); + $writer->writeAttribute('path', $test->getName()); + $writer->endElement(); + } else { + continue; + } + } + + if ($currentTestCase !== null) { + $writer->endElement(); + } + + $writer->endElement(); + + return $writer->outputMemory(); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Exception; + +/** + * Command-line options parsing class. + */ +final class Getopt +{ + /** + * @throws Exception + */ + public static function getopt(array $args, string $short_options, array $long_options = null): array + { + if (empty($args)) { + return [[], []]; + } + + $opts = []; + $non_opts = []; + + if ($long_options) { + \sort($long_options); + } + + if (isset($args[0][0]) && $args[0][0] !== '-') { + \array_shift($args); + } + + \reset($args); + + $args = \array_map('trim', $args); + + /* @noinspection ComparisonOperandsOrderInspection */ + while (false !== $arg = \current($args)) { + $i = \key($args); + \next($args); + + if ($arg === '') { + continue; + } + + if ($arg === '--') { + $non_opts = \array_merge($non_opts, \array_slice($args, $i + 1)); + + break; + } + + if ($arg[0] !== '-' || (\strlen($arg) > 1 && $arg[1] === '-' && !$long_options)) { + $non_opts[] = $args[$i]; + + continue; + } + + if (\strlen($arg) > 1 && $arg[1] === '-') { + self::parseLongOption( + \substr($arg, 2), + $long_options, + $opts, + $args + ); + } else { + self::parseShortOption( + \substr($arg, 1), + $short_options, + $opts, + $args + ); + } + } + + return [$opts, $non_opts]; + } + + /** + * @throws Exception + */ + private static function parseShortOption(string $arg, string $short_options, array &$opts, array &$args): void + { + $argLen = \strlen($arg); + + for ($i = 0; $i < $argLen; $i++) { + $opt = $arg[$i]; + $opt_arg = null; + + if ($arg[$i] === ':' || ($spec = \strstr($short_options, $opt)) === false) { + throw new Exception( + "unrecognized option -- $opt" + ); + } + + if (\strlen($spec) > 1 && $spec[1] === ':') { + if ($i + 1 < $argLen) { + $opts[] = [$opt, \substr($arg, $i + 1)]; + + break; + } + + if (!(\strlen($spec) > 2 && $spec[2] === ':')) { + /* @noinspection ComparisonOperandsOrderInspection */ + if (false === $opt_arg = \current($args)) { + throw new Exception( + "option requires an argument -- $opt" + ); + } + + \next($args); + } + } + + $opts[] = [$opt, $opt_arg]; + } + } + + /** + * @throws Exception + */ + private static function parseLongOption(string $arg, array $long_options, array &$opts, array &$args): void + { + $count = \count($long_options); + $list = \explode('=', $arg); + $opt = $list[0]; + $opt_arg = null; + + if (\count($list) > 1) { + $opt_arg = $list[1]; + } + + $opt_len = \strlen($opt); + + for ($i = 0; $i < $count; $i++) { + $long_opt = $long_options[$i]; + $opt_start = \substr($long_opt, 0, $opt_len); + + if ($opt_start !== $opt) { + continue; + } + + $opt_rest = \substr($long_opt, $opt_len); + + if ($opt_rest !== '' && $i + 1 < $count && $opt[0] !== '=' && + \strpos($long_options[$i + 1], $opt) === 0) { + throw new Exception( + "option --$opt is ambiguous" + ); + } + + if (\substr($long_opt, -1) === '=') { + /* @noinspection StrlenInEmptyStringCheckContextInspection */ + if (\substr($long_opt, -2) !== '==' && !\strlen($opt_arg)) { + /* @noinspection ComparisonOperandsOrderInspection */ + if (false === $opt_arg = \current($args)) { + throw new Exception( + "option --$opt requires an argument" + ); + } + + \next($args); + } + } elseif ($opt_arg) { + throw new Exception( + "option --$opt doesn't allow an argument" + ); + } + + $full_option = '--' . \preg_replace('/={1,2}$/', '', $long_opt); + $opts[] = [$full_option, $opt_arg]; + + return; + } + + throw new Exception("unrecognized option --$opt"); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use Composer\Autoload\ClassLoader; +use DeepCopy\DeepCopy; +use Doctrine\Instantiator\Instantiator; +use PharIo\Manifest\Manifest; +use PharIo\Version\Version as PharIoVersion; +use PHP_Token; +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\Project; +use phpDocumentor\Reflection\Type; +use PHPUnit\Framework\TestCase; +use Prophecy\Prophet; +use ReflectionClass; +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeUnitReverseLookup\Wizard; +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Diff\Diff; +use SebastianBergmann\Environment\Runtime; +use SebastianBergmann\Exporter\Exporter; +use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; +use SebastianBergmann\GlobalState\Snapshot; +use SebastianBergmann\Invoker\Invoker; +use SebastianBergmann\ObjectEnumerator\Enumerator; +use SebastianBergmann\RecursionContext\Context; +use SebastianBergmann\ResourceOperations\ResourceOperations; +use SebastianBergmann\Timer\Timer; +use SebastianBergmann\Version; +use Text_Template; +use TheSeer\Tokenizer\Tokenizer; +use Webmozart\Assert\Assert; + +/** + * Utility class for blacklisting PHPUnit's own source code files. + */ +final class Blacklist +{ + /** + * @var array + */ + public static $blacklistedClassNames = [ + // composer + ClassLoader::class => 1, + + // doctrine/instantiator + Instantiator::class => 1, + + // myclabs/deepcopy + DeepCopy::class => 1, + + // phar-io/manifest + Manifest::class => 1, + + // phar-io/version + PharIoVersion::class => 1, + + // phpdocumentor/reflection-common + Project::class => 1, + + // phpdocumentor/reflection-docblock + DocBlock::class => 1, + + // phpdocumentor/type-resolver + Type::class => 1, + + // phpspec/prophecy + Prophet::class => 1, + + // phpunit/phpunit + TestCase::class => 2, + + // phpunit/php-code-coverage + CodeCoverage::class => 1, + + // phpunit/php-file-iterator + FileIteratorFacade::class => 1, + + // phpunit/php-invoker + Invoker::class => 1, + + // phpunit/php-text-template + Text_Template::class => 1, + + // phpunit/php-timer + Timer::class => 1, + + // phpunit/php-token-stream + PHP_Token::class => 1, + + // sebastian/code-unit-reverse-lookup + Wizard::class => 1, + + // sebastian/comparator + Comparator::class => 1, + + // sebastian/diff + Diff::class => 1, + + // sebastian/environment + Runtime::class => 1, + + // sebastian/exporter + Exporter::class => 1, + + // sebastian/global-state + Snapshot::class => 1, + + // sebastian/object-enumerator + Enumerator::class => 1, + + // sebastian/recursion-context + Context::class => 1, + + // sebastian/resource-operations + ResourceOperations::class => 1, + + // sebastian/version + Version::class => 1, + + // theseer/tokenizer + Tokenizer::class => 1, + + // webmozart/assert + Assert::class => 1, + ]; + + /** + * @var string[] + */ + private static $directories; + + /** + * @return string[] + */ + public function getBlacklistedDirectories(): array + { + $this->initialize(); + + return self::$directories; + } + + public function isBlacklisted(string $file): bool + { + if (\defined('PHPUNIT_TESTSUITE')) { + return false; + } + + $this->initialize(); + + foreach (self::$directories as $directory) { + if (\strpos($file, $directory) === 0) { + return true; + } + } + + return false; + } + + private function initialize(): void + { + if (self::$directories === null) { + self::$directories = []; + + foreach (self::$blacklistedClassNames as $className => $parent) { + if (!\class_exists($className)) { + continue; + } + + $reflector = new ReflectionClass($className); + $directory = $reflector->getFileName(); + + for ($i = 0; $i < $parent; $i++) { + $directory = \dirname($directory); + } + + self::$directories[] = $directory; + } + + // Hide process isolation workaround on Windows. + if (\DIRECTORY_SEPARATOR === '\\') { + // tempnam() prefix is limited to first 3 chars. + // @see https://php.net/manual/en/function.tempnam.php + self::$directories[] = \sys_get_temp_dir() . '\\PHP'; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +final class XdebugFilterScriptGenerator +{ + public function generate(array $filterData): string + { + $items = $this->getWhitelistItems($filterData); + + $files = \array_map( + function ($item) { + return \sprintf( + " '%s'", + $item + ); + }, + $items + ); + + $files = \implode(",\n", $files); + + return << + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +final class Type +{ + public static function isType(string $type): bool + { + switch ($type) { + case 'numeric': + case 'integer': + case 'int': + case 'iterable': + case 'float': + case 'string': + case 'boolean': + case 'bool': + case 'null': + case 'array': + case 'object': + case 'resource': + case 'scalar': + return true; + + default: + return false; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +class NullTestResultCache implements TestResultCacheInterface +{ + public function getState($testName): int + { + return BaseTestRunner::STATUS_UNKNOWN; + } + + public function getTime($testName): float + { + return 0; + } + + public function load(): void + { + } + + public function persist(): void + { + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Exception; + +/** + * Utility class that can print to STDOUT or write to a file. + */ +class Printer +{ + /** + * If true, flush output after every write. + * + * @var bool + */ + protected $autoFlush = false; + + /** + * @var resource + */ + protected $out; + + /** + * @var string + */ + protected $outTarget; + + /** + * Constructor. + * + * @param null|mixed $out + * + * @throws Exception + */ + public function __construct($out = null) + { + if ($out !== null) { + if (\is_string($out)) { + if (\strpos($out, 'socket://') === 0) { + $out = \explode(':', \str_replace('socket://', '', $out)); + + if (\count($out) !== 2) { + throw new Exception; + } + + $this->out = \fsockopen($out[0], $out[1]); + } else { + if (\strpos($out, 'php://') === false && !Filesystem::createDirectory(\dirname($out))) { + throw new Exception(\sprintf('Directory "%s" was not created', \dirname($out))); + } + + $this->out = \fopen($out, 'wt'); + } + + $this->outTarget = $out; + } else { + $this->out = $out; + } + } + } + + /** + * Flush buffer and close output if it's not to a PHP stream + */ + public function flush(): void + { + if ($this->out && \strncmp($this->outTarget, 'php://', 6) !== 0) { + \fclose($this->out); + } + } + + /** + * Performs a safe, incremental flush. + * + * Do not confuse this function with the flush() function of this class, + * since the flush() function may close the file being written to, rendering + * the current object no longer usable. + */ + public function incrementalFlush(): void + { + if ($this->out) { + \fflush($this->out); + } else { + \flush(); + } + } + + public function write(string $buffer): void + { + if ($this->out) { + \fwrite($this->out, $buffer); + + if ($this->autoFlush) { + $this->incrementalFlush(); + } + } else { + if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') { + $buffer = \htmlspecialchars($buffer, \ENT_SUBSTITUTE); + } + + print $buffer; + + if ($this->autoFlush) { + $this->incrementalFlush(); + } + } + } + + /** + * Check auto-flush mode. + */ + public function getAutoFlush(): bool + { + return $this->autoFlush; + } + + /** + * Set auto-flushing mode. + * + * If set, *incremental* flushes will be done after each write. This should + * not be confused with the different effects of this class' flush() method. + */ + public function setAutoFlush(bool $autoFlush): void + { + $this->autoFlush = $autoFlush; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Log; + +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\ExceptionWrapper; +use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestFailure; +use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\TextUI\ResultPrinter; +use PHPUnit\Util\Filter; +use ReflectionClass; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * A TestListener that generates a logfile of the test execution using the + * TeamCity format (for use with PhpStorm, for instance). + */ +class TeamCity extends ResultPrinter +{ + /** + * @var bool + */ + private $isSummaryTestCountPrinted = false; + + /** + * @var string + */ + private $startedTestName; + + /** + * @var false|int + */ + private $flowId; + + public function printResult(TestResult $result): void + { + $this->printHeader(); + $this->printFooter($result); + } + + /** + * An error occurred. + * + * @throws \InvalidArgumentException + */ + public function addError(Test $test, \Throwable $t, float $time): void + { + $this->printEvent( + 'testFailed', + [ + 'name' => $test->getName(), + 'message' => self::getMessage($t), + 'details' => self::getDetails($t), + 'duration' => self::toMilliseconds($time), + ] + ); + } + + /** + * A warning occurred. + * + * @throws \InvalidArgumentException + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + $this->printEvent( + 'testFailed', + [ + 'name' => $test->getName(), + 'message' => self::getMessage($e), + 'details' => self::getDetails($e), + 'duration' => self::toMilliseconds($time), + ] + ); + } + + /** + * A failure occurred. + * + * @throws \InvalidArgumentException + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $parameters = [ + 'name' => $test->getName(), + 'message' => self::getMessage($e), + 'details' => self::getDetails($e), + 'duration' => self::toMilliseconds($time), + ]; + + if ($e instanceof ExpectationFailedException) { + $comparisonFailure = $e->getComparisonFailure(); + + if ($comparisonFailure instanceof ComparisonFailure) { + $expectedString = $comparisonFailure->getExpectedAsString(); + + if ($expectedString === null || empty($expectedString)) { + $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected()); + } + + $actualString = $comparisonFailure->getActualAsString(); + + if ($actualString === null || empty($actualString)) { + $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual()); + } + + if ($actualString !== null && $expectedString !== null) { + $parameters['type'] = 'comparisonFailure'; + $parameters['actual'] = $actualString; + $parameters['expected'] = $expectedString; + } + } + } + + $this->printEvent('testFailed', $parameters); + } + + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + $this->printIgnoredTest($test->getName(), $t, $time); + } + + /** + * Risky test. + * + * @throws \InvalidArgumentException + */ + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + $this->addError($test, $t, $time); + } + + /** + * Skipped test. + * + * @throws \ReflectionException + */ + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + $testName = $test->getName(); + + if ($this->startedTestName !== $testName) { + $this->startTest($test); + $this->printIgnoredTest($testName, $t, $time); + $this->endTest($test, $time); + } else { + $this->printIgnoredTest($testName, $t, $time); + } + } + + public function printIgnoredTest($testName, \Throwable $t, float $time): void + { + $this->printEvent( + 'testIgnored', + [ + 'name' => $testName, + 'message' => self::getMessage($t), + 'details' => self::getDetails($t), + 'duration' => self::toMilliseconds($time), + ] + ); + } + + /** + * A testsuite started. + * + * @throws \ReflectionException + */ + public function startTestSuite(TestSuite $suite): void + { + if (\stripos(\ini_get('disable_functions'), 'getmypid') === false) { + $this->flowId = \getmypid(); + } else { + $this->flowId = false; + } + + if (!$this->isSummaryTestCountPrinted) { + $this->isSummaryTestCountPrinted = true; + + $this->printEvent( + 'testCount', + ['count' => \count($suite)] + ); + } + + $suiteName = $suite->getName(); + + if (empty($suiteName)) { + return; + } + + $parameters = ['name' => $suiteName]; + + if (\class_exists($suiteName, false)) { + $fileName = self::getFileName($suiteName); + $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName"; + } else { + $split = \explode('::', $suiteName); + + if (\count($split) === 2 && \method_exists($split[0], $split[1])) { + $fileName = self::getFileName($split[0]); + $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName"; + $parameters['name'] = $split[1]; + } + } + + $this->printEvent('testSuiteStarted', $parameters); + } + + /** + * A testsuite ended. + */ + public function endTestSuite(TestSuite $suite): void + { + $suiteName = $suite->getName(); + + if (empty($suiteName)) { + return; + } + + $parameters = ['name' => $suiteName]; + + if (!\class_exists($suiteName, false)) { + $split = \explode('::', $suiteName); + + if (\count($split) === 2 && \method_exists($split[0], $split[1])) { + $parameters['name'] = $split[1]; + } + } + + $this->printEvent('testSuiteFinished', $parameters); + } + + /** + * A test started. + * + * @throws \ReflectionException + */ + public function startTest(Test $test): void + { + $testName = $test->getName(); + $this->startedTestName = $testName; + $params = ['name' => $testName]; + + if ($test instanceof TestCase) { + $className = \get_class($test); + $fileName = self::getFileName($className); + $params['locationHint'] = "php_qn://$fileName::\\$className::$testName"; + } + + $this->printEvent('testStarted', $params); + } + + /** + * A test ended. + */ + public function endTest(Test $test, float $time): void + { + parent::endTest($test, $time); + + $this->printEvent( + 'testFinished', + [ + 'name' => $test->getName(), + 'duration' => self::toMilliseconds($time), + ] + ); + } + + protected function writeProgress(string $progress): void + { + } + + /** + * @param string $eventName + * @param array $params + */ + private function printEvent($eventName, $params = []): void + { + $this->write("\n##teamcity[$eventName"); + + if ($this->flowId) { + $params['flowId'] = $this->flowId; + } + + foreach ($params as $key => $value) { + $escapedValue = self::escapeValue($value); + $this->write(" $key='$escapedValue'"); + } + + $this->write("]\n"); + } + + private static function getMessage(\Throwable $t): string + { + $message = ''; + + if ($t instanceof ExceptionWrapper) { + if ($t->getClassName() !== '') { + $message .= $t->getClassName(); + } + + if ($message !== '' && $t->getMessage() !== '') { + $message .= ' : '; + } + } + + return $message . $t->getMessage(); + } + + /** + * @throws \InvalidArgumentException + */ + private static function getDetails(\Throwable $t): string + { + $stackTrace = Filter::getFilteredStacktrace($t); + $previous = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious(); + + while ($previous) { + $stackTrace .= "\nCaused by\n" . + TestFailure::exceptionToString($previous) . "\n" . + Filter::getFilteredStacktrace($previous); + + $previous = $previous instanceof ExceptionWrapper ? + $previous->getPreviousWrapped() : $previous->getPrevious(); + } + + return ' ' . \str_replace("\n", "\n ", $stackTrace); + } + + private static function getPrimitiveValueAsString($value): ?string + { + if ($value === null) { + return 'null'; + } + + if (\is_bool($value)) { + return $value === true ? 'true' : 'false'; + } + + if (\is_scalar($value)) { + return \print_r($value, true); + } + + return null; + } + + private static function escapeValue(string $text): string + { + return \str_replace( + ['|', "'", "\n", "\r", ']', '['], + ['||', "|'", '|n', '|r', '|]', '|['], + $text + ); + } + + /** + * @param string $className + * + * @throws \ReflectionException + */ + private static function getFileName($className): string + { + $reflectionClass = new ReflectionClass($className); + + return $reflectionClass->getFileName(); + } + + /** + * @param float $time microseconds + */ + private static function toMilliseconds(float $time): int + { + return \round($time * 1000); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util\Log; + +use DOMDocument; +use DOMElement; +use PHPUnit\Framework\AssertionFailedError; +use PHPUnit\Framework\ExceptionWrapper; +use PHPUnit\Framework\SelfDescribing; +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestFailure; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Framework\Warning; +use PHPUnit\Util\Filter; +use PHPUnit\Util\Printer; +use PHPUnit\Util\Xml; +use ReflectionClass; +use ReflectionException; + +/** + * A TestListener that generates a logfile of the test execution in XML markup. + * + * The XML markup used is the same as the one that is used by the JUnit Ant task. + */ +class JUnit extends Printer implements TestListener +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * @var DOMElement + */ + protected $root; + + /** + * @var bool + */ + protected $reportUselessTests = false; + + /** + * @var bool + */ + protected $writeDocument = true; + + /** + * @var DOMElement[] + */ + protected $testSuites = []; + + /** + * @var int[] + */ + protected $testSuiteTests = [0]; + + /** + * @var int[] + */ + protected $testSuiteAssertions = [0]; + + /** + * @var int[] + */ + protected $testSuiteErrors = [0]; + + /** + * @var int[] + */ + protected $testSuiteFailures = [0]; + + /** + * @var int[] + */ + protected $testSuiteSkipped = [0]; + + /** + * @var int[] + */ + protected $testSuiteTimes = [0]; + + /** + * @var int + */ + protected $testSuiteLevel = 0; + + /** + * @var DOMElement + */ + protected $currentTestCase; + + /** + * Constructor. + * + * @param null|mixed $out + * + * @throws \PHPUnit\Framework\Exception + */ + public function __construct($out = null, bool $reportUselessTests = false) + { + $this->document = new DOMDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + + $this->root = $this->document->createElement('testsuites'); + $this->document->appendChild($this->root); + + parent::__construct($out); + + $this->reportUselessTests = $reportUselessTests; + } + + /** + * Flush buffer and close output. + */ + public function flush(): void + { + if ($this->writeDocument === true) { + $this->write($this->getXML()); + } + + parent::flush(); + } + + /** + * An error occurred. + * + * @throws \InvalidArgumentException + */ + public function addError(Test $test, \Throwable $t, float $time): void + { + $this->doAddFault($test, $t, $time, 'error'); + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + + /** + * A warning occurred. + * + * @throws \InvalidArgumentException + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + $this->doAddFault($test, $e, $time, 'warning'); + $this->testSuiteFailures[$this->testSuiteLevel]++; + } + + /** + * A failure occurred. + * + * @throws \InvalidArgumentException + */ + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $this->doAddFault($test, $e, $time, 'failure'); + $this->testSuiteFailures[$this->testSuiteLevel]++; + } + + /** + * Incomplete test. + */ + public function addIncompleteTest(Test $test, \Throwable $t, float $time): void + { + $this->doAddSkipped($test); + } + + /** + * Risky test. + */ + public function addRiskyTest(Test $test, \Throwable $t, float $time): void + { + if (!$this->reportUselessTests || $this->currentTestCase === null) { + return; + } + + $error = $this->document->createElement( + 'error', + Xml::prepareString( + "Risky Test\n" . + Filter::getFilteredStacktrace($t) + ) + ); + + $error->setAttribute('type', \get_class($t)); + + $this->currentTestCase->appendChild($error); + + $this->testSuiteErrors[$this->testSuiteLevel]++; + } + + /** + * Skipped test. + */ + public function addSkippedTest(Test $test, \Throwable $t, float $time): void + { + $this->doAddSkipped($test); + } + + /** + * A testsuite started. + */ + public function startTestSuite(TestSuite $suite): void + { + $testSuite = $this->document->createElement('testsuite'); + $testSuite->setAttribute('name', $suite->getName()); + + if (\class_exists($suite->getName(), false)) { + try { + $class = new ReflectionClass($suite->getName()); + + $testSuite->setAttribute('file', $class->getFileName()); + } catch (ReflectionException $e) { + } + } + + if ($this->testSuiteLevel > 0) { + $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); + } else { + $this->root->appendChild($testSuite); + } + + $this->testSuiteLevel++; + $this->testSuites[$this->testSuiteLevel] = $testSuite; + $this->testSuiteTests[$this->testSuiteLevel] = 0; + $this->testSuiteAssertions[$this->testSuiteLevel] = 0; + $this->testSuiteErrors[$this->testSuiteLevel] = 0; + $this->testSuiteFailures[$this->testSuiteLevel] = 0; + $this->testSuiteSkipped[$this->testSuiteLevel] = 0; + $this->testSuiteTimes[$this->testSuiteLevel] = 0; + } + + /** + * A testsuite ended. + */ + public function endTestSuite(TestSuite $suite): void + { + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'tests', + $this->testSuiteTests[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'assertions', + $this->testSuiteAssertions[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'errors', + $this->testSuiteErrors[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'failures', + $this->testSuiteFailures[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'skipped', + $this->testSuiteSkipped[$this->testSuiteLevel] + ); + + $this->testSuites[$this->testSuiteLevel]->setAttribute( + 'time', + \sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]) + ); + + if ($this->testSuiteLevel > 1) { + $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; + $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; + $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; + $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; + $this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel]; + $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; + } + + $this->testSuiteLevel--; + } + + /** + * A test started. + * + * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException + * @throws ReflectionException + */ + public function startTest(Test $test): void + { + $usesDataprovider = false; + + if (\method_exists($test, 'usesDataProvider')) { + $usesDataprovider = $test->usesDataProvider(); + } + + $testCase = $this->document->createElement('testcase'); + $testCase->setAttribute('name', $test->getName()); + + $class = new ReflectionClass($test); + $methodName = $test->getName(!$usesDataprovider); + + if ($class->hasMethod($methodName)) { + $method = $class->getMethod($methodName); + + $testCase->setAttribute('class', $class->getName()); + $testCase->setAttribute('classname', \str_replace('\\', '.', $class->getName())); + $testCase->setAttribute('file', $class->getFileName()); + $testCase->setAttribute('line', $method->getStartLine()); + } + + $this->currentTestCase = $testCase; + } + + /** + * A test ended. + */ + public function endTest(Test $test, float $time): void + { + $numAssertions = 0; + + if (\method_exists($test, 'getNumAssertions')) { + $numAssertions = $test->getNumAssertions(); + } + + $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; + + $this->currentTestCase->setAttribute( + 'assertions', + $numAssertions + ); + + $this->currentTestCase->setAttribute( + 'time', + \sprintf('%F', $time) + ); + + $this->testSuites[$this->testSuiteLevel]->appendChild( + $this->currentTestCase + ); + + $this->testSuiteTests[$this->testSuiteLevel]++; + $this->testSuiteTimes[$this->testSuiteLevel] += $time; + + $testOutput = ''; + + if (\method_exists($test, 'hasOutput') && \method_exists($test, 'getActualOutput')) { + $testOutput = $test->hasOutput() ? $test->getActualOutput() : ''; + } + + if (!empty($testOutput)) { + $systemOut = $this->document->createElement( + 'system-out', + Xml::prepareString($testOutput) + ); + + $this->currentTestCase->appendChild($systemOut); + } + + $this->currentTestCase = null; + } + + /** + * Returns the XML as a string. + */ + public function getXML(): string + { + return $this->document->saveXML(); + } + + /** + * Enables or disables the writing of the document + * in flush(). + * + * This is a "hack" needed for the integration of + * PHPUnit with Phing. + */ + public function setWriteDocument(/*bool*/ $flag): void + { + if (\is_bool($flag)) { + $this->writeDocument = $flag; + } + } + + /** + * Method which generalizes addError() and addFailure() + * + * @throws \InvalidArgumentException + */ + private function doAddFault(Test $test, \Throwable $t, float $time, $type): void + { + if ($this->currentTestCase === null) { + return; + } + + if ($test instanceof SelfDescribing) { + $buffer = $test->toString() . "\n"; + } else { + $buffer = ''; + } + + $buffer .= TestFailure::exceptionToString($t) . "\n" . + Filter::getFilteredStacktrace($t); + + $fault = $this->document->createElement( + $type, + Xml::prepareString($buffer) + ); + + if ($t instanceof ExceptionWrapper) { + $fault->setAttribute('type', $t->getClassName()); + } else { + $fault->setAttribute('type', \get_class($t)); + } + + $this->currentTestCase->appendChild($fault); + } + + private function doAddSkipped(Test $test): void + { + if ($this->currentTestCase === null) { + return; + } + + $skipped = $this->document->createElement('skipped'); + $this->currentTestCase->appendChild($skipped); + + $this->testSuiteSkipped[$this->testSuiteLevel]++; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +interface TestResultCacheInterface +{ + public function getState($testName): int; + + public function getTime($testName): float; + + public function load(): void; + + public function persist(): void; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use DOMElement; +use DOMXPath; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\TestSuiteSorter; +use PHPUnit\TextUI\ResultPrinter; +use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; + +/** + * Wrapper for the PHPUnit XML configuration file. + * + * Example XML configuration file: + * + * + * + * + * + * + * /path/to/files + * /path/to/MyTest.php + * /path/to/files/exclude + * + * + * + * + * + * name + * + * + * name + * + * + * + * + * + * name + * + * + * name + * + * + * + * + * + * /path/to/files + * /path/to/file + * + * /path/to/files + * /path/to/file + * + * + * + * + * + * + * + * + * + * Sebastian + * + * + * 22 + * April + * 19.78 + * + * + * MyRelativeFile.php + * MyRelativeDir + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * . + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +final class Configuration +{ + /** + * @var self[] + */ + private static $instances = []; + + /** + * @var \DOMDocument + */ + private $document; + + /** + * @var DOMXPath + */ + private $xpath; + + /** + * @var string + */ + private $filename; + + /** + * @var \LibXMLError[] + */ + private $errors = []; + + /** + * Returns a PHPUnit configuration object. + * + * @throws Exception + */ + public static function getInstance(string $filename): self + { + $realPath = \realpath($filename); + + if ($realPath === false) { + throw new Exception( + \sprintf( + 'Could not read "%s".', + $filename + ) + ); + } + + /** @var string $realPath */ + if (!isset(self::$instances[$realPath])) { + self::$instances[$realPath] = new self($realPath); + } + + return self::$instances[$realPath]; + } + + /** + * Loads a PHPUnit configuration file. + * + * @throws Exception + */ + private function __construct(string $filename) + { + $this->filename = $filename; + $this->document = Xml::loadFile($filename, false, true, true); + $this->xpath = new DOMXPath($this->document); + + $this->validateConfigurationAgainstSchema(); + } + + /** + * @codeCoverageIgnore + */ + private function __clone() + { + } + + public function hasValidationErrors(): bool + { + return \count($this->errors) > 0; + } + + public function getValidationErrors(): array + { + $result = []; + + foreach ($this->errors as $error) { + if (!isset($result[$error->line])) { + $result[$error->line] = []; + } + $result[$error->line][] = \trim($error->message); + } + + return $result; + } + + /** + * Returns the real path to the configuration file. + */ + public function getFilename(): string + { + return $this->filename; + } + + public function getExtensionConfiguration(): array + { + $result = []; + + foreach ($this->xpath->query('extensions/extension') as $extension) { + /** @var DOMElement $extension */ + $class = (string) $extension->getAttribute('class'); + $file = ''; + $arguments = $this->getConfigurationArguments($extension->childNodes); + + if ($extension->getAttribute('file')) { + $file = $this->toAbsolutePath( + (string) $extension->getAttribute('file'), + true + ); + } + $result[] = [ + 'class' => $class, + 'file' => $file, + 'arguments' => $arguments, + ]; + } + + return $result; + } + + /** + * Returns the configuration for SUT filtering. + */ + public function getFilterConfiguration(): array + { + $addUncoveredFilesFromWhitelist = true; + $processUncoveredFilesFromWhitelist = false; + $includeDirectory = []; + $includeFile = []; + $excludeDirectory = []; + $excludeFile = []; + + $tmp = $this->xpath->query('filter/whitelist'); + + if ($tmp->length === 1) { + if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { + $addUncoveredFilesFromWhitelist = $this->getBoolean( + (string) $tmp->item(0)->getAttribute( + 'addUncoveredFilesFromWhitelist' + ), + true + ); + } + + if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { + $processUncoveredFilesFromWhitelist = $this->getBoolean( + (string) $tmp->item(0)->getAttribute( + 'processUncoveredFilesFromWhitelist' + ), + false + ); + } + + $includeDirectory = $this->readFilterDirectories( + 'filter/whitelist/directory' + ); + + $includeFile = $this->readFilterFiles( + 'filter/whitelist/file' + ); + + $excludeDirectory = $this->readFilterDirectories( + 'filter/whitelist/exclude/directory' + ); + + $excludeFile = $this->readFilterFiles( + 'filter/whitelist/exclude/file' + ); + } + + return [ + 'whitelist' => [ + 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, + 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, + 'include' => [ + 'directory' => $includeDirectory, + 'file' => $includeFile, + ], + 'exclude' => [ + 'directory' => $excludeDirectory, + 'file' => $excludeFile, + ], + ], + ]; + } + + /** + * Returns the configuration for groups. + */ + public function getGroupConfiguration(): array + { + return $this->parseGroupConfiguration('groups'); + } + + /** + * Returns the configuration for testdox groups. + */ + public function getTestdoxGroupConfiguration(): array + { + return $this->parseGroupConfiguration('testdoxGroups'); + } + + /** + * Returns the configuration for listeners. + */ + public function getListenerConfiguration(): array + { + $result = []; + + foreach ($this->xpath->query('listeners/listener') as $listener) { + /** @var DOMElement $listener */ + $class = (string) $listener->getAttribute('class'); + $file = ''; + $arguments = $this->getConfigurationArguments($listener->childNodes); + + if ($listener->getAttribute('file')) { + $file = $this->toAbsolutePath( + (string) $listener->getAttribute('file'), + true + ); + } + + $result[] = [ + 'class' => $class, + 'file' => $file, + 'arguments' => $arguments, + ]; + } + + return $result; + } + + /** + * Returns the logging configuration. + */ + public function getLoggingConfiguration(): array + { + $result = []; + + foreach ($this->xpath->query('logging/log') as $log) { + /** @var DOMElement $log */ + $type = (string) $log->getAttribute('type'); + $target = (string) $log->getAttribute('target'); + + if (!$target) { + continue; + } + + $target = $this->toAbsolutePath($target); + + if ($type === 'coverage-html') { + if ($log->hasAttribute('lowUpperBound')) { + $result['lowUpperBound'] = $this->getInteger( + (string) $log->getAttribute('lowUpperBound'), + 50 + ); + } + + if ($log->hasAttribute('highLowerBound')) { + $result['highLowerBound'] = $this->getInteger( + (string) $log->getAttribute('highLowerBound'), + 90 + ); + } + } elseif ($type === 'coverage-crap4j') { + if ($log->hasAttribute('threshold')) { + $result['crap4jThreshold'] = $this->getInteger( + (string) $log->getAttribute('threshold'), + 30 + ); + } + } elseif ($type === 'coverage-text') { + if ($log->hasAttribute('showUncoveredFiles')) { + $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( + (string) $log->getAttribute('showUncoveredFiles'), + false + ); + } + + if ($log->hasAttribute('showOnlySummary')) { + $result['coverageTextShowOnlySummary'] = $this->getBoolean( + (string) $log->getAttribute('showOnlySummary'), + false + ); + } + } + + $result[$type] = $target; + } + + return $result; + } + + /** + * Returns the PHP configuration. + */ + public function getPHPConfiguration(): array + { + $result = [ + 'include_path' => [], + 'ini' => [], + 'const' => [], + 'var' => [], + 'env' => [], + 'post' => [], + 'get' => [], + 'cookie' => [], + 'server' => [], + 'files' => [], + 'request' => [], + ]; + + foreach ($this->xpath->query('php/includePath') as $includePath) { + $path = (string) $includePath->textContent; + + if ($path) { + $result['include_path'][] = $this->toAbsolutePath($path); + } + } + + foreach ($this->xpath->query('php/ini') as $ini) { + /** @var DOMElement $ini */ + $name = (string) $ini->getAttribute('name'); + $value = (string) $ini->getAttribute('value'); + + $result['ini'][$name]['value'] = $value; + } + + foreach ($this->xpath->query('php/const') as $const) { + /** @var DOMElement $const */ + $name = (string) $const->getAttribute('name'); + $value = (string) $const->getAttribute('value'); + + $result['const'][$name]['value'] = $this->getBoolean($value, $value); + } + + foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { + foreach ($this->xpath->query('php/' . $array) as $var) { + /** @var DOMElement $var */ + $name = (string) $var->getAttribute('name'); + $value = (string) $var->getAttribute('value'); + $verbatim = false; + + if ($var->hasAttribute('verbatim')) { + $verbatim = $this->getBoolean($var->getAttribute('verbatim'), false); + $result[$array][$name]['verbatim'] = $verbatim; + } + + if ($var->hasAttribute('force')) { + $force = $this->getBoolean($var->getAttribute('force'), false); + $result[$array][$name]['force'] = $force; + } + + if (!$verbatim) { + $value = $this->getBoolean($value, $value); + } + + $result[$array][$name]['value'] = $value; + } + } + + return $result; + } + + /** + * Handles the PHP configuration. + */ + public function handlePHPConfiguration(): void + { + $configuration = $this->getPHPConfiguration(); + + if (!empty($configuration['include_path'])) { + \ini_set( + 'include_path', + \implode(\PATH_SEPARATOR, $configuration['include_path']) . + \PATH_SEPARATOR . + \ini_get('include_path') + ); + } + + foreach ($configuration['ini'] as $name => $data) { + $value = $data['value']; + + if (\defined($value)) { + $value = (string) \constant($value); + } + + \ini_set($name, $value); + } + + foreach ($configuration['const'] as $name => $data) { + $value = $data['value']; + + if (!\defined($name)) { + \define($name, $value); + } + } + + foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { + /* + * @see https://github.com/sebastianbergmann/phpunit/issues/277 + */ + switch ($array) { + case 'var': + $target = &$GLOBALS; + + break; + + case 'server': + $target = &$_SERVER; + + break; + + default: + $target = &$GLOBALS['_' . \strtoupper($array)]; + + break; + } + + foreach ($configuration[$array] as $name => $data) { + $target[$name] = $data['value']; + } + } + + foreach ($configuration['env'] as $name => $data) { + $value = $data['value']; + $force = $data['force'] ?? false; + + if ($force || \getenv($name) === false) { + \putenv("{$name}={$value}"); + } + + $value = \getenv($name); + + if (!isset($_ENV[$name])) { + $_ENV[$name] = $value; + } + + if ($force === true) { + $_ENV[$name] = $value; + } + } + } + + /** + * Returns the PHPUnit configuration. + */ + public function getPHPUnitConfiguration(): array + { + $result = []; + $root = $this->document->documentElement; + + if ($root->hasAttribute('cacheTokens')) { + $result['cacheTokens'] = $this->getBoolean( + (string) $root->getAttribute('cacheTokens'), + false + ); + } + + if ($root->hasAttribute('columns')) { + $columns = (string) $root->getAttribute('columns'); + + if ($columns === 'max') { + $result['columns'] = 'max'; + } else { + $result['columns'] = $this->getInteger($columns, 80); + } + } + + if ($root->hasAttribute('colors')) { + /* only allow boolean for compatibility with previous versions + 'always' only allowed from command line */ + if ($this->getBoolean($root->getAttribute('colors'), false)) { + $result['colors'] = ResultPrinter::COLOR_AUTO; + } else { + $result['colors'] = ResultPrinter::COLOR_NEVER; + } + } + + /* + * @see https://github.com/sebastianbergmann/phpunit/issues/657 + */ + if ($root->hasAttribute('stderr')) { + $result['stderr'] = $this->getBoolean( + (string) $root->getAttribute('stderr'), + false + ); + } + + if ($root->hasAttribute('backupGlobals')) { + $result['backupGlobals'] = $this->getBoolean( + (string) $root->getAttribute('backupGlobals'), + false + ); + } + + if ($root->hasAttribute('backupStaticAttributes')) { + $result['backupStaticAttributes'] = $this->getBoolean( + (string) $root->getAttribute('backupStaticAttributes'), + false + ); + } + + if ($root->getAttribute('bootstrap')) { + $result['bootstrap'] = $this->toAbsolutePath( + (string) $root->getAttribute('bootstrap') + ); + } + + if ($root->hasAttribute('convertDeprecationsToExceptions')) { + $result['convertDeprecationsToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertDeprecationsToExceptions'), + true + ); + } + + if ($root->hasAttribute('convertErrorsToExceptions')) { + $result['convertErrorsToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertErrorsToExceptions'), + true + ); + } + + if ($root->hasAttribute('convertNoticesToExceptions')) { + $result['convertNoticesToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertNoticesToExceptions'), + true + ); + } + + if ($root->hasAttribute('convertWarningsToExceptions')) { + $result['convertWarningsToExceptions'] = $this->getBoolean( + (string) $root->getAttribute('convertWarningsToExceptions'), + true + ); + } + + if ($root->hasAttribute('forceCoversAnnotation')) { + $result['forceCoversAnnotation'] = $this->getBoolean( + (string) $root->getAttribute('forceCoversAnnotation'), + false + ); + } + + if ($root->hasAttribute('disableCodeCoverageIgnore')) { + $result['disableCodeCoverageIgnore'] = $this->getBoolean( + (string) $root->getAttribute('disableCodeCoverageIgnore'), + false + ); + } + + if ($root->hasAttribute('processIsolation')) { + $result['processIsolation'] = $this->getBoolean( + (string) $root->getAttribute('processIsolation'), + false + ); + } + + if ($root->hasAttribute('stopOnDefect')) { + $result['stopOnDefect'] = $this->getBoolean( + (string) $root->getAttribute('stopOnDefect'), + false + ); + } + + if ($root->hasAttribute('stopOnError')) { + $result['stopOnError'] = $this->getBoolean( + (string) $root->getAttribute('stopOnError'), + false + ); + } + + if ($root->hasAttribute('stopOnFailure')) { + $result['stopOnFailure'] = $this->getBoolean( + (string) $root->getAttribute('stopOnFailure'), + false + ); + } + + if ($root->hasAttribute('stopOnWarning')) { + $result['stopOnWarning'] = $this->getBoolean( + (string) $root->getAttribute('stopOnWarning'), + false + ); + } + + if ($root->hasAttribute('stopOnIncomplete')) { + $result['stopOnIncomplete'] = $this->getBoolean( + (string) $root->getAttribute('stopOnIncomplete'), + false + ); + } + + if ($root->hasAttribute('stopOnRisky')) { + $result['stopOnRisky'] = $this->getBoolean( + (string) $root->getAttribute('stopOnRisky'), + false + ); + } + + if ($root->hasAttribute('stopOnSkipped')) { + $result['stopOnSkipped'] = $this->getBoolean( + (string) $root->getAttribute('stopOnSkipped'), + false + ); + } + + if ($root->hasAttribute('failOnWarning')) { + $result['failOnWarning'] = $this->getBoolean( + (string) $root->getAttribute('failOnWarning'), + false + ); + } + + if ($root->hasAttribute('failOnRisky')) { + $result['failOnRisky'] = $this->getBoolean( + (string) $root->getAttribute('failOnRisky'), + false + ); + } + + if ($root->hasAttribute('testSuiteLoaderClass')) { + $result['testSuiteLoaderClass'] = (string) $root->getAttribute( + 'testSuiteLoaderClass' + ); + } + + if ($root->hasAttribute('defaultTestSuite')) { + $result['defaultTestSuite'] = (string) $root->getAttribute( + 'defaultTestSuite' + ); + } + + if ($root->getAttribute('testSuiteLoaderFile')) { + $result['testSuiteLoaderFile'] = $this->toAbsolutePath( + (string) $root->getAttribute('testSuiteLoaderFile') + ); + } + + if ($root->hasAttribute('printerClass')) { + $result['printerClass'] = (string) $root->getAttribute( + 'printerClass' + ); + } + + if ($root->getAttribute('printerFile')) { + $result['printerFile'] = $this->toAbsolutePath( + (string) $root->getAttribute('printerFile') + ); + } + + if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) { + $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutChangesToGlobalState'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutOutputDuringTests')) { + $result['disallowTestOutput'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutOutputDuringTests'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) { + $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) { + $result['reportUselessTests'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'), + true + ); + } + + if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { + $result['disallowTodoAnnotatedTests'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'), + false + ); + } + + if ($root->hasAttribute('beStrictAboutCoversAnnotation')) { + $result['strictCoverage'] = $this->getBoolean( + (string) $root->getAttribute('beStrictAboutCoversAnnotation'), + false + ); + } + + if ($root->hasAttribute('defaultTimeLimit')) { + $result['defaultTimeLimit'] = $this->getInteger( + (string) $root->getAttribute('defaultTimeLimit'), + 1 + ); + } + + if ($root->hasAttribute('enforceTimeLimit')) { + $result['enforceTimeLimit'] = $this->getBoolean( + (string) $root->getAttribute('enforceTimeLimit'), + false + ); + } + + if ($root->hasAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage')) { + $result['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $this->getBoolean( + (string) $root->getAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage'), + false + ); + } + + if ($root->hasAttribute('timeoutForSmallTests')) { + $result['timeoutForSmallTests'] = $this->getInteger( + (string) $root->getAttribute('timeoutForSmallTests'), + 1 + ); + } + + if ($root->hasAttribute('timeoutForMediumTests')) { + $result['timeoutForMediumTests'] = $this->getInteger( + (string) $root->getAttribute('timeoutForMediumTests'), + 10 + ); + } + + if ($root->hasAttribute('timeoutForLargeTests')) { + $result['timeoutForLargeTests'] = $this->getInteger( + (string) $root->getAttribute('timeoutForLargeTests'), + 60 + ); + } + + if ($root->hasAttribute('reverseDefectList')) { + $result['reverseDefectList'] = $this->getBoolean( + (string) $root->getAttribute('reverseDefectList'), + false + ); + } + + if ($root->hasAttribute('verbose')) { + $result['verbose'] = $this->getBoolean( + (string) $root->getAttribute('verbose'), + false + ); + } + + if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) { + $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean( + (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'), + false + ); + } + + if ($root->hasAttribute('extensionsDirectory')) { + $result['extensionsDirectory'] = $this->toAbsolutePath( + (string) $root->getAttribute( + 'extensionsDirectory' + ) + ); + } + + if ($root->hasAttribute('cacheResult')) { + $result['cacheResult'] = $this->getBoolean( + (string) $root->getAttribute('cacheResult'), + false + ); + } + + if ($root->hasAttribute('cacheResultFile')) { + $result['cacheResultFile'] = $this->toAbsolutePath( + (string) $root->getAttribute('cacheResultFile') + ); + } + + if ($root->hasAttribute('executionOrder')) { + foreach (\explode(',', $root->getAttribute('executionOrder')) as $order) { + switch ($order) { + case 'default': + $result['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT; + $result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT; + $result['resolveDependencies'] = false; + + break; + case 'reverse': + $result['executionOrder'] = TestSuiteSorter::ORDER_REVERSED; + + break; + case 'random': + $result['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED; + + break; + case 'defects': + $result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST; + + break; + case 'depends': + $result['resolveDependencies'] = true; + + break; + } + } + } + + if ($root->hasAttribute('resolveDependencies')) { + $result['resolveDependencies'] = $this->getBoolean( + (string) $root->getAttribute('resolveDependencies'), + false + ); + } + + return $result; + } + + /** + * Returns the test suite configuration. + * + * @throws Exception + */ + public function getTestSuiteConfiguration(string $testSuiteFilter = ''): TestSuite + { + $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); + + if ($testSuiteNodes->length === 0) { + $testSuiteNodes = $this->xpath->query('testsuite'); + } + + if ($testSuiteNodes->length === 1) { + return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); + } + + $suite = new TestSuite; + + foreach ($testSuiteNodes as $testSuiteNode) { + $suite->addTestSuite( + $this->getTestSuite($testSuiteNode, $testSuiteFilter) + ); + } + + return $suite; + } + + /** + * Returns the test suite names from the configuration. + */ + public function getTestSuiteNames(): array + { + $names = []; + + foreach ($this->xpath->query('*/testsuite') as $node) { + /* @var DOMElement $node */ + $names[] = $node->getAttribute('name'); + } + + return $names; + } + + private function validateConfigurationAgainstSchema(): void + { + $original = \libxml_use_internal_errors(true); + $xsdFilename = __DIR__ . '/../../phpunit.xsd'; + + if (\defined('__PHPUNIT_PHAR_ROOT__')) { + $xsdFilename = __PHPUNIT_PHAR_ROOT__ . '/phpunit.xsd'; + } + + $this->document->schemaValidate($xsdFilename); + $this->errors = \libxml_get_errors(); + \libxml_clear_errors(); + \libxml_use_internal_errors($original); + } + + /** + * Collects and returns the configuration arguments from the PHPUnit + * XML configuration + */ + private function getConfigurationArguments(\DOMNodeList $nodes): array + { + $arguments = []; + + if ($nodes->length === 0) { + return $arguments; + } + + foreach ($nodes as $node) { + if (!$node instanceof DOMElement) { + continue; + } + + if ($node->tagName !== 'arguments') { + continue; + } + + foreach ($node->childNodes as $argument) { + if (!$argument instanceof DOMElement) { + continue; + } + + if ($argument->tagName === 'file' || $argument->tagName === 'directory') { + $arguments[] = $this->toAbsolutePath((string) $argument->textContent); + } else { + $arguments[] = Xml::xmlToVariable($argument); + } + } + } + + return $arguments; + } + + /** + * @throws \PHPUnit\Framework\Exception + */ + private function getTestSuite(DOMElement $testSuiteNode, string $testSuiteFilter = ''): TestSuite + { + if ($testSuiteNode->hasAttribute('name')) { + $suite = new TestSuite( + (string) $testSuiteNode->getAttribute('name') + ); + } else { + $suite = new TestSuite; + } + + $exclude = []; + + foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) { + $excludeFile = (string) $excludeNode->textContent; + + if ($excludeFile) { + $exclude[] = $this->toAbsolutePath($excludeFile); + } + } + + $fileIteratorFacade = new FileIteratorFacade; + $testSuiteFilter = $testSuiteFilter ? \explode(',', $testSuiteFilter) : []; + + foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) { + /** @var DOMElement $directoryNode */ + if (!empty($testSuiteFilter) && !\in_array($directoryNode->parentNode->getAttribute('name'), $testSuiteFilter)) { + continue; + } + + $directory = (string) $directoryNode->textContent; + + if (empty($directory)) { + continue; + } + + $prefix = ''; + $suffix = 'Test.php'; + + if (!$this->satisfiesPhpVersion($directoryNode)) { + continue; + } + + if ($directoryNode->hasAttribute('prefix')) { + $prefix = (string) $directoryNode->getAttribute('prefix'); + } + + if ($directoryNode->hasAttribute('suffix')) { + $suffix = (string) $directoryNode->getAttribute('suffix'); + } + + $files = $fileIteratorFacade->getFilesAsArray( + $this->toAbsolutePath($directory), + $suffix, + $prefix, + $exclude + ); + + $suite->addTestFiles($files); + } + + foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) { + /** @var DOMElement $fileNode */ + if (!empty($testSuiteFilter) && !\in_array($fileNode->parentNode->getAttribute('name'), $testSuiteFilter)) { + continue; + } + + $file = (string) $fileNode->textContent; + + if (empty($file)) { + continue; + } + + $file = $fileIteratorFacade->getFilesAsArray( + $this->toAbsolutePath($file) + ); + + if (!isset($file[0])) { + continue; + } + + $file = $file[0]; + + if (!$this->satisfiesPhpVersion($fileNode)) { + continue; + } + + $suite->addTestFile($file); + } + + return $suite; + } + + private function satisfiesPhpVersion(DOMElement $node): bool + { + $phpVersion = \PHP_VERSION; + $phpVersionOperator = '>='; + + if ($node->hasAttribute('phpVersion')) { + $phpVersion = (string) $node->getAttribute('phpVersion'); + } + + if ($node->hasAttribute('phpVersionOperator')) { + $phpVersionOperator = (string) $node->getAttribute('phpVersionOperator'); + } + + return \version_compare(\PHP_VERSION, $phpVersion, $phpVersionOperator); + } + + /** + * if $value is 'false' or 'true', this returns the value that $value represents. + * Otherwise, returns $default, which may be a string in rare cases. + * See PHPUnit\Util\ConfigurationTest::testPHPConfigurationIsReadCorrectly + * + * @param bool|string $default + * + * @return bool|string + */ + private function getBoolean(string $value, $default) + { + if (\strtolower($value) === 'false') { + return false; + } + + if (\strtolower($value) === 'true') { + return true; + } + + return $default; + } + + private function getInteger(string $value, int $default): int + { + if (\is_numeric($value)) { + return (int) $value; + } + + return $default; + } + + private function readFilterDirectories(string $query): array + { + $directories = []; + + foreach ($this->xpath->query($query) as $directoryNode) { + /** @var DOMElement $directoryNode */ + $directoryPath = (string) $directoryNode->textContent; + + if (!$directoryPath) { + continue; + } + + $prefix = ''; + $suffix = '.php'; + $group = 'DEFAULT'; + + if ($directoryNode->hasAttribute('prefix')) { + $prefix = (string) $directoryNode->getAttribute('prefix'); + } + + if ($directoryNode->hasAttribute('suffix')) { + $suffix = (string) $directoryNode->getAttribute('suffix'); + } + + if ($directoryNode->hasAttribute('group')) { + $group = (string) $directoryNode->getAttribute('group'); + } + + $directories[] = [ + 'path' => $this->toAbsolutePath($directoryPath), + 'prefix' => $prefix, + 'suffix' => $suffix, + 'group' => $group, + ]; + } + + return $directories; + } + + /** + * @return string[] + */ + private function readFilterFiles(string $query): array + { + $files = []; + + foreach ($this->xpath->query($query) as $file) { + $filePath = (string) $file->textContent; + + if ($filePath) { + $files[] = $this->toAbsolutePath($filePath); + } + } + + return $files; + } + + private function toAbsolutePath(string $path, bool $useIncludePath = false): string + { + $path = \trim($path); + + if ($path[0] === '/') { + return $path; + } + + // Matches the following on Windows: + // - \\NetworkComputer\Path + // - \\.\D: + // - \\.\c: + // - C:\Windows + // - C:\windows + // - C:/windows + // - c:/windows + if (\defined('PHP_WINDOWS_VERSION_BUILD') && + ($path[0] === '\\' || (\strlen($path) >= 3 && \preg_match('#^[A-Z]\:[/\\\]#i', \substr($path, 0, 3))))) { + return $path; + } + + if (\strpos($path, '://') !== false) { + return $path; + } + + $file = \dirname($this->filename) . \DIRECTORY_SEPARATOR . $path; + + if ($useIncludePath && !\file_exists($file)) { + $includePathFile = \stream_resolve_include_path($path); + + if ($includePathFile) { + $file = $includePathFile; + } + } + + return $file; + } + + private function parseGroupConfiguration(string $root): array + { + $groups = [ + 'include' => [], + 'exclude' => [], + ]; + + foreach ($this->xpath->query($root . '/include/group') as $group) { + $groups['include'][] = (string) $group->textContent; + } + + foreach ($this->xpath->query($root . '/exclude/group') as $group) { + $groups['exclude'][] = (string) $group->textContent; + } + + return $groups; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Exception; + +final class Json +{ + /** + * Prettify json string + * + * @throws \PHPUnit\Framework\Exception + */ + public static function prettify(string $json): string + { + $decodedJson = \json_decode($json, true); + + if (\json_last_error()) { + throw new Exception( + 'Cannot prettify invalid json' + ); + } + + return \json_encode($decodedJson, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES); + } + + /* + * To allow comparison of JSON strings, first process them into a consistent + * format so that they can be compared as strings. + * @return array ($error, $canonicalized_json) The $error parameter is used + * to indicate an error decoding the json. This is used to avoid ambiguity + * with JSON strings consisting entirely of 'null' or 'false'. + */ + public static function canonicalize(string $json): array + { + $decodedJson = \json_decode($json); + + if (\json_last_error()) { + return [true, null]; + } + + self::recursiveSort($decodedJson); + + $reencodedJson = \json_encode($decodedJson); + + return [false, $reencodedJson]; + } + + /* + * JSON object keys are unordered while PHP array keys are ordered. + * Sort all array keys to ensure both the expected and actual values have + * their keys in the same order. + */ + private static function recursiveSort(&$json): void + { + if (\is_array($json) === false) { + // If the object is not empty, change it to an associative array + // so we can sort the keys (and we will still re-encode it + // correctly, since PHP encodes associative arrays as JSON objects.) + // But EMPTY objects MUST remain empty objects. (Otherwise we will + // re-encode it as a JSON array rather than a JSON object.) + // See #2919. + if (\is_object($json) && \count((array) $json) > 0) { + $json = (array) $json; + } else { + return; + } + } + + \ksort($json); + + foreach ($json as $key => &$value) { + self::recursiveSort($value); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestSuite; +use PHPUnit\Runner\PhptTestCase; + +final class TextTestListRenderer +{ + public function render(TestSuite $suite): string + { + $buffer = 'Available test(s):' . \PHP_EOL; + + foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) { + if ($test instanceof TestCase) { + $name = \sprintf( + '%s::%s', + \get_class($test), + \str_replace(' with data set ', '', $test->getName()) + ); + } elseif ($test instanceof PhptTestCase) { + $name = $test->getName(); + } else { + continue; + } + + $buffer .= \sprintf( + ' - %s' . \PHP_EOL, + $name + ); + } + + return $buffer; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Runner; + +use PHPUnit\Framework\Test; +use PHPUnit\Util\Filesystem; + +class TestResultCache implements \Serializable, TestResultCacheInterface +{ + /** + * @var string + */ + public const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; + + /** + * Provide extra protection against incomplete or corrupt caches + * + * @var array + */ + private const ALLOWED_CACHE_TEST_STATUSES = [ + BaseTestRunner::STATUS_SKIPPED, + BaseTestRunner::STATUS_INCOMPLETE, + BaseTestRunner::STATUS_FAILURE, + BaseTestRunner::STATUS_ERROR, + BaseTestRunner::STATUS_RISKY, + BaseTestRunner::STATUS_WARNING, + ]; + + /** + * Path and filename for result cache file + * + * @var string + */ + private $cacheFilename; + + /** + * The list of defective tests + * + * + * // Mark a test skipped + * $this->defects[$testName] = BaseTestRunner::TEST_SKIPPED; + * + * + * @var array array + */ + private $defects = []; + + /** + * The list of execution duration of suites and tests (in seconds) + * + * + * // Record running time for test + * $this->times[$testName] = 1.234; + * + * + * @var array + */ + private $times = []; + + public function __construct($filename = null) + { + $this->cacheFilename = $filename ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; + } + + public function persist(): void + { + $this->saveToFile(); + } + + public function saveToFile(): void + { + if (\defined('PHPUNIT_TESTSUITE_RESULTCACHE')) { + return; + } + + if (!Filesystem::createDirectory(\dirname($this->cacheFilename))) { + throw new Exception( + \sprintf( + 'Cannot create directory "%s" for result cache file', + $this->cacheFilename + ) + ); + } + + \file_put_contents( + $this->cacheFilename, + \serialize($this) + ); + } + + public function setState(string $testName, int $state): void + { + if ($state !== BaseTestRunner::STATUS_PASSED) { + $this->defects[$testName] = $state; + } + } + + public function getState($testName): int + { + return $this->defects[$testName] ?? BaseTestRunner::STATUS_UNKNOWN; + } + + public function setTime(string $testName, float $time): void + { + $this->times[$testName] = $time; + } + + public function getTime($testName): float + { + return $this->times[$testName] ?? 0; + } + + public function load(): void + { + $this->clear(); + + if (\is_file($this->cacheFilename) === false) { + return; + } + + $cacheData = @\file_get_contents($this->cacheFilename); + + // @codeCoverageIgnoreStart + if ($cacheData === false) { + return; + } + // @codeCoverageIgnoreEnd + + $cache = @\unserialize($cacheData, ['allowed_classes' => [self::class]]); + + if ($cache === false) { + return; + } + + if ($cache instanceof self) { + /* @var \PHPUnit\Runner\TestResultCache */ + $cache->copyStateToCache($this); + } + } + + public function copyStateToCache(self $targetCache): void + { + foreach ($this->defects as $name => $state) { + $targetCache->setState($name, $state); + } + + foreach ($this->times as $name => $time) { + $targetCache->setTime($name, $time); + } + } + + public function clear(): void + { + $this->defects = []; + $this->times = []; + } + + public function serialize(): string + { + return \serialize([ + 'defects' => $this->defects, + 'times' => $this->times, + ]); + } + + public function unserialize($serialized): void + { + $data = \unserialize($serialized); + + if (isset($data['times'])) { + foreach ($data['times'] as $testName => $testTime) { + $this->times[$testName] = (float) $testTime; + } + } + + if (isset($data['defects'])) { + foreach ($data['defects'] as $testName => $testResult) { + if (\in_array($testResult, self::ALLOWED_CACHE_TEST_STATUSES, true)) { + $this->defects[$testName] = $testResult; + } + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +use PHPUnit\Framework\Exception; + +/** + * Utility methods to load PHP sourcefiles. + */ +final class FileLoader +{ + /** + * Checks if a PHP sourcecode file is readable. The sourcecode file is loaded through the load() method. + * + * As a fallback, PHP looks in the directory of the file executing the stream_resolve_include_path function. + * We do not want to load the Test.php file here, so skip it if it found that. + * PHP prioritizes the include_path setting, so if the current directory is in there, it will first look in the + * current working directory. + * + * @throws Exception + */ + public static function checkAndLoad(string $filename): string + { + $includePathFilename = \stream_resolve_include_path($filename); + $localFile = __DIR__ . \DIRECTORY_SEPARATOR . $filename; + + /** + * @see https://github.com/sebastianbergmann/phpunit/pull/2751 + */ + $isReadable = @\fopen($includePathFilename, 'r') !== false; + + if (!$includePathFilename || !$isReadable || $includePathFilename === $localFile) { + throw new Exception( + \sprintf('Cannot open file "%s".' . "\n", $filename) + ); + } + + self::load($includePathFilename); + + return $includePathFilename; + } + + /** + * Loads a PHP sourcefile. + */ + public static function load(string $filename): void + { + $oldVariableNames = \array_keys(\get_defined_vars()); + + include_once $filename; + + $newVariables = \get_defined_vars(); + $newVariableNames = \array_diff(\array_keys($newVariables), $oldVariableNames); + + foreach ($newVariableNames as $variableName) { + if ($variableName !== 'oldVariableNames') { + $GLOBALS[$variableName] = $newVariables[$variableName]; + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Util; + +/** + * Filesystem helpers. + */ +final class Filesystem +{ + /** + * Maps class names to source file names: + * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php + * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php + */ + public static function classNameToFilename(string $className): string + { + return \str_replace( + ['_', '\\'], + \DIRECTORY_SEPARATOR, + $className + ) . '.php'; + } + + public static function createDirectory(string $directory): bool + { + return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + } +} +. + */ + +namespace Doctrine\Instantiator\Exception; + +use InvalidArgumentException as BaseInvalidArgumentException; +use ReflectionClass; + +/** + * Exception for invalid arguments provided to the instantiator + * + * @author Marco Pivetta + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface +{ + public static function fromNonExistingClass(string $className) : self + { + if (interface_exists($className)) { + return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); + } + + if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { + return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); + } + + return new self(sprintf('The provided class "%s" does not exist', $className)); + } + + public static function fromAbstractClass(ReflectionClass $reflectionClass) : self + { + return new self(sprintf( + 'The provided class "%s" is abstract, and can not be instantiated', + $reflectionClass->getName() + )); + } +} +. + */ + +namespace Doctrine\Instantiator\Exception; + +use Exception; +use ReflectionClass; +use UnexpectedValueException as BaseUnexpectedValueException; + +/** + * Exception for given parameters causing invalid/unexpected state on instantiation + * + * @author Marco Pivetta + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface +{ + public static function fromSerializationTriggeredException( + ReflectionClass $reflectionClass, + Exception $exception + ) : self { + return new self( + sprintf( + 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', + $reflectionClass->getName() + ), + 0, + $exception + ); + } + + public static function fromUncleanUnSerialization( + ReflectionClass $reflectionClass, + string $errorString, + int $errorCode, + string $errorFile, + int $errorLine + ) : self { + return new self( + sprintf( + 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' + . 'in file "%s" at line "%d"', + $reflectionClass->getName(), + $errorFile, + $errorLine + ), + 0, + new Exception($errorString, $errorCode) + ); + } +} +. + */ + +namespace Doctrine\Instantiator\Exception; + +/** + * Base exception marker interface for the instantiator component + * + * @author Marco Pivetta + */ +interface ExceptionInterface +{ +} +. + */ + +namespace Doctrine\Instantiator; + +/** + * Instantiator provides utility methods to build objects without invoking their constructors + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * @param string $className + * + * @return object + * + * @throws \Doctrine\Instantiator\Exception\ExceptionInterface + */ + public function instantiate($className); +} +. + */ + +namespace Doctrine\Instantiator; + +use Doctrine\Instantiator\Exception\InvalidArgumentException; +use Doctrine\Instantiator\Exception\UnexpectedValueException; +use Exception; +use ReflectionClass; + +/** + * {@inheritDoc} + * + * @author Marco Pivetta + */ +final class Instantiator implements InstantiatorInterface +{ + /** + * Markers used internally by PHP to define whether {@see \unserialize} should invoke + * the method {@see \Serializable::unserialize()} when dealing with classes implementing + * the {@see \Serializable} interface. + */ + const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; + const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; + + /** + * @var \callable[] used to instantiate specific classes, indexed by class name + */ + private static $cachedInstantiators = []; + + /** + * @var object[] of objects that can directly be cloned, indexed by class name + */ + private static $cachedCloneables = []; + + /** + * {@inheritDoc} + */ + public function instantiate($className) + { + if (isset(self::$cachedCloneables[$className])) { + return clone self::$cachedCloneables[$className]; + } + + if (isset(self::$cachedInstantiators[$className])) { + $factory = self::$cachedInstantiators[$className]; + + return $factory(); + } + + return $this->buildAndCacheFromFactory($className); + } + + /** + * Builds the requested object and caches it in static properties for performance + * + * @return object + */ + private function buildAndCacheFromFactory(string $className) + { + $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); + $instance = $factory(); + + if ($this->isSafeToClone(new ReflectionClass($instance))) { + self::$cachedCloneables[$className] = clone $instance; + } + + return $instance; + } + + /** + * Builds a callable capable of instantiating the given $className without + * invoking its constructor. + * + * @throws InvalidArgumentException + * @throws UnexpectedValueException + * @throws \ReflectionException + */ + private function buildFactory(string $className) : callable + { + $reflectionClass = $this->getReflectionClass($className); + + if ($this->isInstantiableViaReflection($reflectionClass)) { + return [$reflectionClass, 'newInstanceWithoutConstructor']; + } + + $serializedString = sprintf( + '%s:%d:"%s":0:{}', + self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, + strlen($className), + $className + ); + + $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); + + return function () use ($serializedString) { + return unserialize($serializedString); + }; + } + + /** + * @param string $className + * + * @return ReflectionClass + * + * @throws InvalidArgumentException + * @throws \ReflectionException + */ + private function getReflectionClass($className) : ReflectionClass + { + if (! class_exists($className)) { + throw InvalidArgumentException::fromNonExistingClass($className); + } + + $reflection = new ReflectionClass($className); + + if ($reflection->isAbstract()) { + throw InvalidArgumentException::fromAbstractClass($reflection); + } + + return $reflection; + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) : void + { + set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) : void { + $error = UnexpectedValueException::fromUncleanUnSerialization( + $reflectionClass, + $message, + $code, + $file, + $line + ); + }); + + $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); + + restore_error_handler(); + + if ($error) { + throw $error; + } + } + + /** + * @param ReflectionClass $reflectionClass + * @param string $serializedString + * + * @throws UnexpectedValueException + * + * @return void + */ + private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) : void + { + try { + unserialize($serializedString); + } catch (Exception $exception) { + restore_error_handler(); + + throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); + } + } + + private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool + { + return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); + } + + /** + * Verifies whether the given class is to be considered internal + */ + private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool + { + do { + if ($reflectionClass->isInternal()) { + return true; + } + } while ($reflectionClass = $reflectionClass->getParentClass()); + + return false; + } + + /** + * Checks if a class is cloneable + * + * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. + */ + private function isSafeToClone(ReflectionClass $reflection) : bool + { + return $reflection->isCloneable() && ! $reflection->hasMethod('__clone'); + } +} +Copyright (c) 2014 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +/** + * The location where an element occurs within a file. + */ +final class Location +{ + /** @var int */ + private $lineNumber = 0; + + /** @var int */ + private $columnNumber = 0; + + /** + * Initializes the location for an element using its line number in the file and optionally the column number. + * + * @param int $lineNumber + * @param int $columnNumber + */ + public function __construct($lineNumber, $columnNumber = 0) + { + $this->lineNumber = $lineNumber; + $this->columnNumber = $columnNumber; + } + + /** + * Returns the line number that is covered by this location. + * + * @return integer + */ + public function getLineNumber() + { + return $this->lineNumber; + } + + /** + * Returns the column number (character position on a line) for this location object. + * + * @return integer + */ + public function getColumnNumber() + { + return $this->columnNumber; + } +} +fqsen = $fqsen; + + if (isset($matches[2])) { + $this->name = $matches[2]; + } else { + $matches = explode('\\', $fqsen); + $this->name = trim(end($matches), '()'); + } + } + + /** + * converts this class to string. + * + * @return string + */ + public function __toString() + { + return $this->fqsen; + } + + /** + * Returns the name of the element without path. + * + * @return string + */ + public function getName() + { + return $this->name; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +/** + * Interface for files processed by the ProjectFactory + */ +interface File +{ + /** + * Returns the content of the file as a string. + * + * @return string + */ + public function getContents(); + + /** + * Returns md5 hash of the file. + * + * @return string + */ + public function md5(); + + /** + * Returns an relative path to the file. + * + * @return string + */ + public function path(); +} +The MIT License (MIT) + +Copyright (c) 2015 phpDocumentor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\GlobalState; + +/** + * Exports parts of a Snapshot as PHP code. + */ +class CodeExporter +{ + public function constants(Snapshot $snapshot): string + { + $result = ''; + + foreach ($snapshot->constants() as $name => $value) { + $result .= \sprintf( + 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", + $name, + $name, + $this->exportVariable($value) + ); + } + + return $result; + } + + public function globalVariables(Snapshot $snapshot): string + { + $result = '$GLOBALS = [];' . PHP_EOL; + + foreach ($snapshot->globalVariables() as $name => $value) { + $result .= \sprintf( + '$GLOBALS[%s] = %s;' . PHP_EOL, + $this->exportVariable($name), + $this->exportVariable($value) + ); + } + + return $result; + } + + public function iniSettings(Snapshot $snapshot): string + { + $result = ''; + + foreach ($snapshot->iniSettings() as $key => $value) { + $result .= \sprintf( + '@ini_set(%s, %s);' . "\n", + $this->exportVariable($key), + $this->exportVariable($value) + ); + } + + return $result; + } + + private function exportVariable($variable): string + { + if (\is_scalar($variable) || \is_null($variable) || + (\is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { + return \var_export($variable, true); + } + + return 'unserialize(' . \var_export(\serialize($variable), true) . ')'; + } + + private function arrayOnlyContainsScalars(array $array): bool + { + $result = true; + + foreach ($array as $element) { + if (\is_array($element)) { + $result = self::arrayOnlyContainsScalars($element); + } elseif (!\is_scalar($element) && !\is_null($element)) { + $result = false; + } + + if ($result === false) { + break; + } + } + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\GlobalState; + +use ReflectionProperty; + +/** + * Restorer of snapshots of global state. + */ +class Restorer +{ + /** + * Deletes function definitions that are not defined in a snapshot. + * + * @throws RuntimeException when the uopz_delete() function is not available + * + * @see https://github.com/krakjoe/uopz + */ + public function restoreFunctions(Snapshot $snapshot) + { + if (!\function_exists('uopz_delete')) { + throw new RuntimeException('The uopz_delete() function is required for this operation'); + } + + $functions = \get_defined_functions(); + + foreach (\array_diff($functions['user'], $snapshot->functions()) as $function) { + uopz_delete($function); + } + } + + /** + * Restores all global and super-global variables from a snapshot. + */ + public function restoreGlobalVariables(Snapshot $snapshot) + { + $superGlobalArrays = $snapshot->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); + } + + $globalVariables = $snapshot->globalVariables(); + + foreach (\array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !\in_array($key, $superGlobalArrays) && + !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { + if (\array_key_exists($key, $globalVariables)) { + $GLOBALS[$key] = $globalVariables[$key]; + } else { + unset($GLOBALS[$key]); + } + } + } + } + + /** + * Restores all static attributes in user-defined classes from this snapshot. + */ + public function restoreStaticAttributes(Snapshot $snapshot) + { + $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false); + $newClasses = \array_diff($current->classes(), $snapshot->classes()); + + unset($current); + + foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { + foreach ($staticAttributes as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setAccessible(true); + $reflector->setValue($value); + } + } + + foreach ($newClasses as $className) { + $class = new \ReflectionClass($className); + $defaults = $class->getDefaultProperties(); + + foreach ($class->getProperties() as $attribute) { + if (!$attribute->isStatic()) { + continue; + } + + $name = $attribute->getName(); + + if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + if (!isset($defaults[$name])) { + continue; + } + + $attribute->setAccessible(true); + $attribute->setValue($defaults[$name]); + } + } + } + + /** + * Restores a super-global variable array from this snapshot. + */ + private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray) + { + $superGlobalVariables = $snapshot->superGlobalVariables(); + + if (isset($GLOBALS[$superGlobalArray]) && + \is_array($GLOBALS[$superGlobalArray]) && + isset($superGlobalVariables[$superGlobalArray])) { + $keys = \array_keys( + \array_merge( + $GLOBALS[$superGlobalArray], + $superGlobalVariables[$superGlobalArray] + ) + ); + + foreach ($keys as $key) { + if (isset($superGlobalVariables[$superGlobalArray][$key])) { + $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; + } else { + unset($GLOBALS[$superGlobalArray][$key]); + } + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; +use Serializable; + +/** + * A snapshot of global state. + */ +class Snapshot +{ + /** + * @var Blacklist + */ + private $blacklist; + + /** + * @var array + */ + private $globalVariables = []; + + /** + * @var array + */ + private $superGlobalArrays = []; + + /** + * @var array + */ + private $superGlobalVariables = []; + + /** + * @var array + */ + private $staticAttributes = []; + + /** + * @var array + */ + private $iniSettings = []; + + /** + * @var array + */ + private $includedFiles = []; + + /** + * @var array + */ + private $constants = []; + + /** + * @var array + */ + private $functions = []; + + /** + * @var array + */ + private $interfaces = []; + + /** + * @var array + */ + private $classes = []; + + /** + * @var array + */ + private $traits = []; + + /** + * Creates a snapshot of the current global state. + */ + public function __construct(Blacklist $blacklist = null, bool $includeGlobalVariables = true, bool $includeStaticAttributes = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true) + { + if ($blacklist === null) { + $blacklist = new Blacklist; + } + + $this->blacklist = $blacklist; + + if ($includeConstants) { + $this->snapshotConstants(); + } + + if ($includeFunctions) { + $this->snapshotFunctions(); + } + + if ($includeClasses || $includeStaticAttributes) { + $this->snapshotClasses(); + } + + if ($includeInterfaces) { + $this->snapshotInterfaces(); + } + + if ($includeGlobalVariables) { + $this->setupSuperGlobalArrays(); + $this->snapshotGlobals(); + } + + if ($includeStaticAttributes) { + $this->snapshotStaticAttributes(); + } + + if ($includeIniSettings) { + $this->iniSettings = \ini_get_all(null, false); + } + + if ($includeIncludedFiles) { + $this->includedFiles = \get_included_files(); + } + + $this->traits = \get_declared_traits(); + } + + public function blacklist(): Blacklist + { + return $this->blacklist; + } + + public function globalVariables(): array + { + return $this->globalVariables; + } + + public function superGlobalVariables(): array + { + return $this->superGlobalVariables; + } + + public function superGlobalArrays(): array + { + return $this->superGlobalArrays; + } + + public function staticAttributes(): array + { + return $this->staticAttributes; + } + + public function iniSettings(): array + { + return $this->iniSettings; + } + + public function includedFiles(): array + { + return $this->includedFiles; + } + + public function constants(): array + { + return $this->constants; + } + + public function functions(): array + { + return $this->functions; + } + + public function interfaces(): array + { + return $this->interfaces; + } + + public function classes(): array + { + return $this->classes; + } + + public function traits(): array + { + return $this->traits; + } + + /** + * Creates a snapshot user-defined constants. + */ + private function snapshotConstants() + { + $constants = \get_defined_constants(true); + + if (isset($constants['user'])) { + $this->constants = $constants['user']; + } + } + + /** + * Creates a snapshot user-defined functions. + */ + private function snapshotFunctions() + { + $functions = \get_defined_functions(); + + $this->functions = $functions['user']; + } + + /** + * Creates a snapshot user-defined classes. + */ + private function snapshotClasses() + { + foreach (\array_reverse(\get_declared_classes()) as $className) { + $class = new ReflectionClass($className); + + if (!$class->isUserDefined()) { + break; + } + + $this->classes[] = $className; + } + + $this->classes = \array_reverse($this->classes); + } + + /** + * Creates a snapshot user-defined interfaces. + */ + private function snapshotInterfaces() + { + foreach (\array_reverse(\get_declared_interfaces()) as $interfaceName) { + $class = new ReflectionClass($interfaceName); + + if (!$class->isUserDefined()) { + break; + } + + $this->interfaces[] = $interfaceName; + } + + $this->interfaces = \array_reverse($this->interfaces); + } + + /** + * Creates a snapshot of all global and super-global variables. + */ + private function snapshotGlobals() + { + $superGlobalArrays = $this->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->snapshotSuperGlobalArray($superGlobalArray); + } + + foreach (\array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !\in_array($key, $superGlobalArrays) && + $this->canBeSerialized($GLOBALS[$key]) && + !$this->blacklist->isGlobalVariableBlacklisted($key)) { + $this->globalVariables[$key] = \unserialize(\serialize($GLOBALS[$key])); + } + } + } + + /** + * Creates a snapshot a super-global variable array. + */ + private function snapshotSuperGlobalArray(string $superGlobalArray) + { + $this->superGlobalVariables[$superGlobalArray] = []; + + if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + $this->superGlobalVariables[$superGlobalArray][$key] = \unserialize(\serialize($value)); + } + } + } + + /** + * Creates a snapshot of all static attributes in user-defined classes. + */ + private function snapshotStaticAttributes() + { + foreach ($this->classes as $className) { + $class = new ReflectionClass($className); + $snapshot = []; + + foreach ($class->getProperties() as $attribute) { + if ($attribute->isStatic()) { + $name = $attribute->getName(); + + if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + $attribute->setAccessible(true); + $value = $attribute->getValue(); + + if ($this->canBeSerialized($value)) { + $snapshot[$name] = \unserialize(\serialize($value)); + } + } + } + + if (!empty($snapshot)) { + $this->staticAttributes[$className] = $snapshot; + } + } + } + + /** + * Returns a list of all super-global variable arrays. + */ + private function setupSuperGlobalArrays() + { + $this->superGlobalArrays = [ + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST' + ]; + + if (\ini_get('register_long_arrays') == '1') { + $this->superGlobalArrays = \array_merge( + $this->superGlobalArrays, + [ + 'HTTP_ENV_VARS', + 'HTTP_POST_VARS', + 'HTTP_GET_VARS', + 'HTTP_COOKIE_VARS', + 'HTTP_SERVER_VARS', + 'HTTP_POST_FILES' + ] + ); + } + } + + /** + * @todo Implement this properly + */ + private function canBeSerialized($variable): bool + { + if (!\is_object($variable)) { + return !\is_resource($variable); + } + + if ($variable instanceof \stdClass) { + return true; + } + + $class = new ReflectionClass($variable); + + do { + if ($class->isInternal()) { + return $variable instanceof Serializable; + } + } while ($class = $class->getParentClass()); + + return true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; + +/** + * A blacklist for global state elements that should not be snapshotted. + */ +class Blacklist +{ + /** + * @var array + */ + private $globalVariables = []; + + /** + * @var string[] + */ + private $classes = []; + + /** + * @var string[] + */ + private $classNamePrefixes = []; + + /** + * @var string[] + */ + private $parentClasses = []; + + /** + * @var string[] + */ + private $interfaces = []; + + /** + * @var array + */ + private $staticAttributes = []; + + public function addGlobalVariable(string $variableName) + { + $this->globalVariables[$variableName] = true; + } + + public function addClass(string $className) + { + $this->classes[] = $className; + } + + public function addSubclassesOf(string $className) + { + $this->parentClasses[] = $className; + } + + public function addImplementorsOf(string $interfaceName) + { + $this->interfaces[] = $interfaceName; + } + + public function addClassNamePrefix(string $classNamePrefix) + { + $this->classNamePrefixes[] = $classNamePrefix; + } + + public function addStaticAttribute(string $className, string $attributeName) + { + if (!isset($this->staticAttributes[$className])) { + $this->staticAttributes[$className] = []; + } + + $this->staticAttributes[$className][$attributeName] = true; + } + + public function isGlobalVariableBlacklisted(string $variableName): bool + { + return isset($this->globalVariables[$variableName]); + } + + public function isStaticAttributeBlacklisted(string $className, string $attributeName): bool + { + if (\in_array($className, $this->classes)) { + return true; + } + + foreach ($this->classNamePrefixes as $prefix) { + if (\strpos($className, $prefix) === 0) { + return true; + } + } + + $class = new ReflectionClass($className); + + foreach ($this->parentClasses as $type) { + if ($class->isSubclassOf($type)) { + return true; + } + } + + foreach ($this->interfaces as $type) { + if ($class->implementsInterface($type)) { + return true; + } + } + + if (isset($this->staticAttributes[$className][$attributeName])) { + return true; + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\GlobalState; + +class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\GlobalState; + +interface Exception +{ +} +sebastian/global-state + +Copyright (c) 2001-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +callback = $callable; + } + + /** + * {@inheritdoc} + */ + public function apply($element) + { + return call_user_func($this->callback, $element); + } +} + $propertyValue) { + $copy->{$propertyName} = $propertyValue; + } + + return $copy; + } +} +copier = $copier; + } + + /** + * {@inheritdoc} + */ + public function apply($element) + { + $newElement = clone $element; + + $copy = $this->createCopyClosure(); + + return $copy($newElement); + } + + private function createCopyClosure() + { + $copier = $this->copier; + + $copy = function (SplDoublyLinkedList $list) use ($copier) { + // Replace each element in the list with a deep copy of itself + for ($i = 1; $i <= $list->count(); $i++) { + $copy = $copier->recursiveCopy($list->shift()); + + $list->push($copy); + } + + return $list; + }; + + return Closure::bind($copy, null, DeepCopy::class); + } +} +getProperties() does not return private properties from ancestor classes. + * + * @author muratyaman@gmail.com + * @see http://php.net/manual/en/reflectionclass.getproperties.php + * + * @param ReflectionClass $ref + * + * @return ReflectionProperty[] + */ + public static function getProperties(ReflectionClass $ref) + { + $props = $ref->getProperties(); + $propsArr = array(); + + foreach ($props as $prop) { + $propertyName = $prop->getName(); + $propsArr[$propertyName] = $prop; + } + + if ($parentClass = $ref->getParentClass()) { + $parentPropsArr = self::getProperties($parentClass); + foreach ($propsArr as $key => $property) { + $parentPropsArr[$key] = $property; + } + + return $parentPropsArr; + } + + return $propsArr; + } + + /** + * Retrieves property by name from object and all its ancestors. + * + * @param object|string $object + * @param string $name + * + * @throws PropertyException + * @throws ReflectionException + * + * @return ReflectionProperty + */ + public static function getProperty($object, $name) + { + $reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); + + if ($reflection->hasProperty($name)) { + return $reflection->getProperty($name); + } + + if ($parentClass = $reflection->getParentClass()) { + return self::getProperty($parentClass->getName(), $name); + } + + throw new PropertyException( + sprintf( + 'The class "%s" doesn\'t have a property with the given name: "%s".', + is_object($object) ? get_class($object) : $object, + $name + ) + ); + } +} +type = $type; + } + + /** + * @param mixed $element + * + * @return boolean + */ + public function matches($element) + { + return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type; + } +} +property = $property; + } + + /** + * Matches a property by its name. + * + * {@inheritdoc} + */ + public function matches($object, $property) + { + return $property == $this->property; + } +} +propertyType = $propertyType; + } + + /** + * {@inheritdoc} + */ + public function matches($object, $property) + { + try { + $reflectionProperty = ReflectionHelper::getProperty($object, $property); + } catch (ReflectionException $exception) { + return false; + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty->getValue($object) instanceof $this->propertyType; + } +} +class = $class; + $this->property = $property; + } + + /** + * Matches a specific property of a specific class. + * + * {@inheritdoc} + */ + public function matches($object, $property) + { + return ($object instanceof $this->class) && $property == $this->property; + } +} +callback = $callable; + } + + /** + * Replaces the object property by the result of the callback called with the object property. + * + * {@inheritdoc} + */ + public function apply($object, $property, $objectCopier) + { + $reflectionProperty = ReflectionHelper::getProperty($object, $property); + $reflectionProperty->setAccessible(true); + + $value = call_user_func($this->callback, $reflectionProperty->getValue($object)); + + $reflectionProperty->setValue($object, $value); + } +} +setAccessible(true); + + $reflectionProperty->setValue($object, new ArrayCollection()); + } +} setAccessible(true); + $oldCollection = $reflectionProperty->getValue($object); + + $newCollection = $oldCollection->map( + function ($item) use ($objectCopier) { + return $objectCopier($item); + } + ); + + $reflectionProperty->setValue($object, $newCollection); + } +} +__load(); + } +} +setAccessible(true); + $reflectionProperty->setValue($object, null); + } +} + Filter, 'matcher' => Matcher] pairs. + */ + private $filters = []; + + /** + * Type Filters to apply. + * + * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. + */ + private $typeFilters = []; + + /** + * @var bool + */ + private $skipUncloneable = false; + + /** + * @var bool + */ + private $useCloneMethod; + + /** + * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used + * instead of the regular deep cloning. + */ + public function __construct($useCloneMethod = false) + { + $this->useCloneMethod = $useCloneMethod; + + $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); + $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); + } + + /** + * If enabled, will not throw an exception when coming across an uncloneable property. + * + * @param $skipUncloneable + * + * @return $this + */ + public function skipUncloneable($skipUncloneable = true) + { + $this->skipUncloneable = $skipUncloneable; + + return $this; + } + + /** + * Deep copies the given object. + * + * @param mixed $object + * + * @return mixed + */ + public function copy($object) + { + $this->hashMap = []; + + return $this->recursiveCopy($object); + } + + public function addFilter(Filter $filter, Matcher $matcher) + { + $this->filters[] = [ + 'matcher' => $matcher, + 'filter' => $filter, + ]; + } + + public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) + { + $this->typeFilters[] = [ + 'matcher' => $matcher, + 'filter' => $filter, + ]; + } + + private function recursiveCopy($var) + { + // Matches Type Filter + if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { + return $filter->apply($var); + } + + // Resource + if (is_resource($var)) { + return $var; + } + + // Array + if (is_array($var)) { + return $this->copyArray($var); + } + + // Scalar + if (! is_object($var)) { + return $var; + } + + // Object + return $this->copyObject($var); + } + + /** + * Copy an array + * @param array $array + * @return array + */ + private function copyArray(array $array) + { + foreach ($array as $key => $value) { + $array[$key] = $this->recursiveCopy($value); + } + + return $array; + } + + /** + * Copies an object. + * + * @param object $object + * + * @throws CloneException + * + * @return object + */ + private function copyObject($object) + { + $objectHash = spl_object_hash($object); + + if (isset($this->hashMap[$objectHash])) { + return $this->hashMap[$objectHash]; + } + + $reflectedObject = new ReflectionObject($object); + $isCloneable = $reflectedObject->isCloneable(); + + if (false === $isCloneable) { + if ($this->skipUncloneable) { + $this->hashMap[$objectHash] = $object; + + return $object; + } + + throw new CloneException( + sprintf( + 'The class "%s" is not cloneable.', + $reflectedObject->getName() + ) + ); + } + + $newObject = clone $object; + $this->hashMap[$objectHash] = $newObject; + + if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) { + return $newObject; + } + + if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) { + return $newObject; + } + + foreach (ReflectionHelper::getProperties($reflectedObject) as $property) { + $this->copyObjectProperty($newObject, $property); + } + + return $newObject; + } + + private function copyObjectProperty($object, ReflectionProperty $property) + { + // Ignore static properties + if ($property->isStatic()) { + return; + } + + // Apply the filters + foreach ($this->filters as $item) { + /** @var Matcher $matcher */ + $matcher = $item['matcher']; + /** @var Filter $filter */ + $filter = $item['filter']; + + if ($matcher->matches($object, $property->getName())) { + $filter->apply( + $object, + $property->getName(), + function ($object) { + return $this->recursiveCopy($object); + } + ); + + // If a filter matches, we stop processing this property + return; + } + } + + $property->setAccessible(true); + $propertyValue = $property->getValue($object); + + // Copy the property + $property->setValue($object, $this->recursiveCopy($propertyValue)); + } + + /** + * Returns first filter that matches variable, `null` if no such filter found. + * + * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and + * 'matcher' with value of type {@see TypeMatcher} + * @param mixed $var + * + * @return TypeFilter|null + */ + private function getFirstMatchedTypeFilter(array $filterRecords, $var) + { + $matched = $this->first( + $filterRecords, + function (array $record) use ($var) { + /* @var TypeMatcher $matcher */ + $matcher = $record['matcher']; + + return $matcher->matches($var); + } + ); + + return isset($matched) ? $matched['filter'] : null; + } + + /** + * Returns first element that matches predicate, `null` if no such element found. + * + * @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs. + * @param callable $predicate Predicate arguments are: element. + * + * @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher' + * with value of type {@see TypeMatcher} or `null`. + */ + private function first(array $elements, callable $predicate) + { + foreach ($elements as $element) { + if (call_user_func($predicate, $element)) { + return $element; + } + } + + return null; + } +} +copy($value); + } +} +The MIT License (MIT) + +Copyright (c) 2013 My C-Sense + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +phpunit/phpunit: 7.5.6 +doctrine/instantiator: 1.1.0 +myclabs/deep-copy: 1.8.1 +phar-io/manifest: 1.0.3 +phar-io/version: 2.0.1 +phpdocumentor/reflection-common: 1.0.1 +phpdocumentor/reflection-docblock: 4.3.0 +phpdocumentor/type-resolver: 0.4.0 +phpspec/prophecy: 1.8.0 +phpunit/php-code-coverage: 6.1.4 +phpunit/php-file-iterator: 2.0.2 +phpunit/php-invoker: 2.0.0 +phpunit/php-text-template: 1.2.1 +phpunit/php-timer: 2.0.0 +phpunit/php-token-stream: 3.0.1 +sebastian/code-unit-reverse-lookup: 1.0.1 +sebastian/comparator: 3.0.2 +sebastian/diff: 3.0.2 +sebastian/environment: 4.1.0 +sebastian/exporter: 3.1.0 +sebastian/global-state: 2.0.0 +sebastian/object-enumerator: 3.0.3 +sebastian/object-reflector: 1.1.1 +sebastian/recursion-context: 3.0.0 +sebastian/resource-operations: 2.0.1 +sebastian/version: 2.0.1 +symfony/polyfill-ctype: v1.10.0 +theseer/tokenizer: 1.1.0 +webmozart/assert: 1.4.0 + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\ObjectReflector; + +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\ObjectReflector; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace SebastianBergmann\ObjectReflector; + +class ObjectReflector +{ + /** + * @param object $object + * + * @return array + * + * @throws InvalidArgumentException + */ + public function getAttributes($object): array + { + if (!is_object($object)) { + throw new InvalidArgumentException; + } + + $attributes = []; + $className = get_class($object); + + foreach ((array) $object as $name => $value) { + $name = explode("\0", (string) $name); + + if (count($name) === 1) { + $name = $name[0]; + } else { + if ($name[1] !== $className) { + $name = $name[1] . '::' . $name[2]; + } else { + $name = $name[2]; + } + } + + $attributes[$name] = $value; + } + + return $attributes; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Invoker; + +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Invoker; + +final class Invoker +{ + /** + * @var int + */ + private $timeout; + + /** + * @throws \Throwable + */ + public function invoke(callable $callable, array $arguments, int $timeout) + { + \pcntl_signal( + \SIGALRM, + function (): void { + throw new TimeoutException( + \sprintf( + 'Execution aborted after %d second%s', + $this->timeout, + $this->timeout === 1 ? '' : 's' + ) + ); + }, + true + ); + + $this->timeout = $timeout; + + \pcntl_async_signals(true); + \pcntl_alarm($timeout); + + try { + $result = \call_user_func_array($callable, $arguments); + } catch (\Throwable $t) { + \pcntl_alarm(0); + + throw $t; + } + + \pcntl_alarm(0); + + return $result; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Invoker; + +final class TimeoutException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Timer; + +final class Timer +{ + /** + * @var array + */ + private static $times = [ + 'hour' => 3600000, + 'minute' => 60000, + 'second' => 1000 + ]; + + /** + * @var array + */ + private static $startTimes = []; + + public static function start(): void + { + self::$startTimes[] = \microtime(true); + } + + public static function stop(): float + { + return \microtime(true) - \array_pop(self::$startTimes); + } + + public static function secondsToTimeString(float $time): string + { + $ms = \round($time * 1000); + + foreach (self::$times as $unit => $value) { + if ($ms >= $value) { + $time = \floor($ms / $value * 100.0) / 100.0; + + return $time . ' ' . ($time == 1 ? $unit : $unit . 's'); + } + } + + return $ms . ' ms'; + } + + /** + * @throws RuntimeException + */ + public static function timeSinceStartOfRequest(): string + { + if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { + $startOfRequest = $_SERVER['REQUEST_TIME_FLOAT']; + } elseif (isset($_SERVER['REQUEST_TIME'])) { + $startOfRequest = $_SERVER['REQUEST_TIME']; + } else { + throw new RuntimeException('Cannot determine time at which the request started'); + } + + return self::secondsToTimeString(\microtime(true) - $startOfRequest); + } + + /** + * @throws RuntimeException + */ + public static function resourceUsage(): string + { + return \sprintf( + 'Time: %s, Memory: %4.2fMB', + self::timeSinceStartOfRequest(), + \memory_get_peak_usage(true) / 1048576 + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Timer; + +final class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Timer; + +interface Exception +{ +} +phpunit/php-timer + +Copyright (c) 2010-2018, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\CodeUnitReverseLookup; + +/** + * @since Class available since Release 1.0.0 + */ +class Wizard +{ + /** + * @var array + */ + private $lookupTable = []; + + /** + * @var array + */ + private $processedClasses = []; + + /** + * @var array + */ + private $processedFunctions = []; + + /** + * @param string $filename + * @param int $lineNumber + * + * @return string + */ + public function lookup($filename, $lineNumber) + { + if (!isset($this->lookupTable[$filename][$lineNumber])) { + $this->updateLookupTable(); + } + + if (isset($this->lookupTable[$filename][$lineNumber])) { + return $this->lookupTable[$filename][$lineNumber]; + } else { + return $filename . ':' . $lineNumber; + } + } + + private function updateLookupTable() + { + $this->processClassesAndTraits(); + $this->processFunctions(); + } + + private function processClassesAndTraits() + { + foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) { + if (isset($this->processedClasses[$classOrTrait])) { + continue; + } + + $reflector = new \ReflectionClass($classOrTrait); + + foreach ($reflector->getMethods() as $method) { + $this->processFunctionOrMethod($method); + } + + $this->processedClasses[$classOrTrait] = true; + } + } + + private function processFunctions() + { + foreach (get_defined_functions()['user'] as $function) { + if (isset($this->processedFunctions[$function])) { + continue; + } + + $this->processFunctionOrMethod(new \ReflectionFunction($function)); + + $this->processedFunctions[$function] = true; + } + } + + /** + * @param \ReflectionFunctionAbstract $functionOrMethod + */ + private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod) + { + if ($functionOrMethod->isInternal()) { + return; + } + + $name = $functionOrMethod->getName(); + + if ($functionOrMethod instanceof \ReflectionMethod) { + $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; + } + + if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { + $this->lookupTable[$functionOrMethod->getFileName()] = []; + } + + foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { + $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; + } + } +} +code-unit-reverse-lookup + +Copyright (c) 2016-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\Tag; +use Webmozart\Assert\Assert; + +final class DocBlock +{ + /** @var string The opening line for this docblock. */ + private $summary = ''; + + /** @var DocBlock\Description The actual description for this docblock. */ + private $description = null; + + /** @var Tag[] An array containing all the tags in this docblock; except inline. */ + private $tags = []; + + /** @var Types\Context Information about the context of this DocBlock. */ + private $context = null; + + /** @var Location Information about the location of this DocBlock. */ + private $location = null; + + /** @var bool Is this DocBlock (the start of) a template? */ + private $isTemplateStart = false; + + /** @var bool Does this DocBlock signify the end of a DocBlock template? */ + private $isTemplateEnd = false; + + /** + * @param string $summary + * @param DocBlock\Description $description + * @param DocBlock\Tag[] $tags + * @param Types\Context $context The context in which the DocBlock occurs. + * @param Location $location The location within the file that this DocBlock occurs in. + * @param bool $isTemplateStart + * @param bool $isTemplateEnd + */ + public function __construct( + $summary = '', + DocBlock\Description $description = null, + array $tags = [], + Types\Context $context = null, + Location $location = null, + $isTemplateStart = false, + $isTemplateEnd = false + ) { + Assert::string($summary); + Assert::boolean($isTemplateStart); + Assert::boolean($isTemplateEnd); + Assert::allIsInstanceOf($tags, Tag::class); + + $this->summary = $summary; + $this->description = $description ?: new DocBlock\Description(''); + foreach ($tags as $tag) { + $this->addTag($tag); + } + + $this->context = $context; + $this->location = $location; + + $this->isTemplateEnd = $isTemplateEnd; + $this->isTemplateStart = $isTemplateStart; + } + + /** + * @return string + */ + public function getSummary() + { + return $this->summary; + } + + /** + * @return DocBlock\Description + */ + public function getDescription() + { + return $this->description; + } + + /** + * Returns the current context. + * + * @return Types\Context + */ + public function getContext() + { + return $this->context; + } + + /** + * Returns the current location. + * + * @return Location + */ + public function getLocation() + { + return $this->location; + } + + /** + * Returns whether this DocBlock is the start of a Template section. + * + * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker + * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. + * + * An example of such an opening is: + * + * ``` + * /**#@+ + * * My DocBlock + * * / + * ``` + * + * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all + * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). + * + * @see self::isTemplateEnd() for the check whether a closing marker was provided. + * + * @return boolean + */ + public function isTemplateStart() + { + return $this->isTemplateStart; + } + + /** + * Returns whether this DocBlock is the end of a Template section. + * + * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. + * + * @return boolean + */ + public function isTemplateEnd() + { + return $this->isTemplateEnd; + } + + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags() + { + return $this->tags; + } + + /** + * Returns an array of tags matching the given name. If no tags are found + * an empty array is returned. + * + * @param string $name String to search by. + * + * @return Tag[] + */ + public function getTagsByName($name) + { + Assert::string($name); + + $result = []; + + /** @var Tag $tag */ + foreach ($this->getTags() as $tag) { + if ($tag->getName() !== $name) { + continue; + } + + $result[] = $tag; + } + + return $result; + } + + /** + * Checks if a tag of a certain type is present in this DocBlock. + * + * @param string $name Tag name to check for. + * + * @return bool + */ + public function hasTag($name) + { + Assert::string($name); + + /** @var Tag $tag */ + foreach ($this->getTags() as $tag) { + if ($tag->getName() === $name) { + return true; + } + } + + return false; + } + + /** + * Remove a tag from this DocBlock. + * + * @param Tag $tag The tag to remove. + * + * @return void + */ + public function removeTag(Tag $tagToRemove) + { + foreach ($this->tags as $key => $tag) { + if ($tag === $tagToRemove) { + unset($this->tags[$key]); + break; + } + } + } + + /** + * Adds a tag to this DocBlock. + * + * @param Tag $tag The tag to add. + * + * @return void + */ + private function addTag(Tag $tag) + { + $this->tags[] = $tag; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\DocBlock\StandardTagFactory; +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\TagFactory; +use Webmozart\Assert\Assert; + +final class DocBlockFactory implements DocBlockFactoryInterface +{ + /** @var DocBlock\DescriptionFactory */ + private $descriptionFactory; + + /** @var DocBlock\TagFactory */ + private $tagFactory; + + /** + * Initializes this factory with the required subcontractors. + * + * @param DescriptionFactory $descriptionFactory + * @param TagFactory $tagFactory + */ + public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory) + { + $this->descriptionFactory = $descriptionFactory; + $this->tagFactory = $tagFactory; + } + + /** + * Factory method for easy instantiation. + * + * @param string[] $additionalTags + * + * @return DocBlockFactory + */ + public static function createInstance(array $additionalTags = []) + { + $fqsenResolver = new FqsenResolver(); + $tagFactory = new StandardTagFactory($fqsenResolver); + $descriptionFactory = new DescriptionFactory($tagFactory); + + $tagFactory->addService($descriptionFactory); + $tagFactory->addService(new TypeResolver($fqsenResolver)); + + $docBlockFactory = new self($descriptionFactory, $tagFactory); + foreach ($additionalTags as $tagName => $tagHandler) { + $docBlockFactory->registerTagHandler($tagName, $tagHandler); + } + + return $docBlockFactory; + } + + /** + * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the + * getDocComment method (such as a ReflectionClass object). + * @param Types\Context $context + * @param Location $location + * + * @return DocBlock + */ + public function create($docblock, Types\Context $context = null, Location $location = null) + { + if (is_object($docblock)) { + if (!method_exists($docblock, 'getDocComment')) { + $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; + throw new \InvalidArgumentException($exceptionMessage); + } + + $docblock = $docblock->getDocComment(); + } + + Assert::stringNotEmpty($docblock); + + if ($context === null) { + $context = new Types\Context(''); + } + + $parts = $this->splitDocBlock($this->stripDocComment($docblock)); + list($templateMarker, $summary, $description, $tags) = $parts; + + return new DocBlock( + $summary, + $description ? $this->descriptionFactory->create($description, $context) : null, + array_filter($this->parseTagBlock($tags, $context), function ($tag) { + return $tag instanceof Tag; + }), + $context, + $location, + $templateMarker === '#@+', + $templateMarker === '#@-' + ); + } + + public function registerTagHandler($tagName, $handler) + { + $this->tagFactory->registerTagHandler($tagName, $handler); + } + + /** + * Strips the asterisks from the DocBlock comment. + * + * @param string $comment String containing the comment text. + * + * @return string + */ + private function stripDocComment($comment) + { + $comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment)); + + // reg ex above is not able to remove */ from a single line docblock + if (substr($comment, -2) === '*/') { + $comment = trim(substr($comment, 0, -2)); + } + + return str_replace(["\r\n", "\r"], "\n", $comment); + } + + /** + * Splits the DocBlock into a template marker, summary, description and block of tags. + * + * @param string $comment Comment to split into the sub-parts. + * + * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. + * @author Mike van Riel for extending the regex with template marker support. + * + * @return string[] containing the template marker (if any), summary, description and a string containing the tags. + */ + private function splitDocBlock($comment) + { + // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This + // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the + // performance impact of running a regular expression + if (strpos($comment, '@') === 0) { + return ['', '', '', $comment]; + } + + // clears all extra horizontal whitespace from the line endings to prevent parsing issues + $comment = preg_replace('/\h*$/Sum', '', $comment); + + /* + * Splits the docblock into a template marker, summary, description and tags section. + * + * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may + * occur after it and will be stripped). + * - The short description is started from the first character until a dot is encountered followed by a + * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing + * errors). This is optional. + * - The long description, any character until a new line is encountered followed by an @ and word + * characters (a tag). This is optional. + * - Tags; the remaining characters + * + * Big thanks to RichardJ for contributing this Regular Expression + */ + preg_match( + '/ + \A + # 1. Extract the template marker + (?:(\#\@\+|\#\@\-)\n?)? + + # 2. Extract the summary + (?: + (?! @\pL ) # The summary may not start with an @ + ( + [^\n.]+ + (?: + (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines + [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line + [^\n.]+ # Include anything else + )* + \.? + )? + ) + + # 3. Extract the description + (?: + \s* # Some form of whitespace _must_ precede a description because a summary must be there + (?! @\pL ) # The description may not start with an @ + ( + [^\n]+ + (?: \n+ + (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line + [^\n]+ # Include anything else + )* + ) + )? + + # 4. Extract the tags (anything that follows) + (\s+ [\s\S]*)? # everything that follows + /ux', + $comment, + $matches + ); + array_shift($matches); + + while (count($matches) < 4) { + $matches[] = ''; + } + + return $matches; + } + + /** + * Creates the tag objects. + * + * @param string $tags Tag block to parse. + * @param Types\Context $context Context of the parsed Tag + * + * @return DocBlock\Tag[] + */ + private function parseTagBlock($tags, Types\Context $context) + { + $tags = $this->filterTagBlock($tags); + if (!$tags) { + return []; + } + + $result = $this->splitTagBlockIntoTagLines($tags); + foreach ($result as $key => $tagLine) { + $result[$key] = $this->tagFactory->create(trim($tagLine), $context); + } + + return $result; + } + + /** + * @param string $tags + * + * @return string[] + */ + private function splitTagBlockIntoTagLines($tags) + { + $result = []; + foreach (explode("\n", $tags) as $tag_line) { + if (isset($tag_line[0]) && ($tag_line[0] === '@')) { + $result[] = $tag_line; + } else { + $result[count($result) - 1] .= "\n" . $tag_line; + } + } + + return $result; + } + + /** + * @param $tags + * @return string + */ + private function filterTagBlock($tags) + { + $tags = trim($tags); + if (!$tags) { + return null; + } + + if ('@' !== $tags[0]) { + // @codeCoverageIgnoreStart + // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that + // we didn't foresee. + throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); + // @codeCoverageIgnoreEnd + } + + return $tags; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock\Tags\Example; + +/** + * Class used to find an example file's location based on a given ExampleDescriptor. + */ +class ExampleFinder +{ + /** @var string */ + private $sourceDirectory = ''; + + /** @var string[] */ + private $exampleDirectories = []; + + /** + * Attempts to find the example contents for the given descriptor. + * + * @param Example $example + * + * @return string + */ + public function find(Example $example) + { + $filename = $example->getFilePath(); + + $file = $this->getExampleFileContents($filename); + if (!$file) { + return "** File not found : {$filename} **"; + } + + return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); + } + + /** + * Registers the project's root directory where an 'examples' folder can be expected. + * + * @param string $directory + * + * @return void + */ + public function setSourceDirectory($directory = '') + { + $this->sourceDirectory = $directory; + } + + /** + * Returns the project's root directory where an 'examples' folder can be expected. + * + * @return string + */ + public function getSourceDirectory() + { + return $this->sourceDirectory; + } + + /** + * Registers a series of directories that may contain examples. + * + * @param string[] $directories + */ + public function setExampleDirectories(array $directories) + { + $this->exampleDirectories = $directories; + } + + /** + * Returns a series of directories that may contain examples. + * + * @return string[] + */ + public function getExampleDirectories() + { + return $this->exampleDirectories; + } + + /** + * Attempts to find the requested example file and returns its contents or null if no file was found. + * + * This method will try several methods in search of the given example file, the first one it encounters is + * returned: + * + * 1. Iterates through all examples folders for the given filename + * 2. Checks the source folder for the given filename + * 3. Checks the 'examples' folder in the current working directory for examples + * 4. Checks the path relative to the current working directory for the given filename + * + * @param string $filename + * + * @return string|null + */ + private function getExampleFileContents($filename) + { + $normalizedPath = null; + + foreach ($this->exampleDirectories as $directory) { + $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); + if (is_readable($exampleFileFromConfig)) { + $normalizedPath = $exampleFileFromConfig; + break; + } + } + + if (!$normalizedPath) { + if (is_readable($this->getExamplePathFromSource($filename))) { + $normalizedPath = $this->getExamplePathFromSource($filename); + } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) { + $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); + } elseif (is_readable($filename)) { + $normalizedPath = $filename; + } + } + + return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null; + } + + /** + * Get example filepath based on the example directory inside your project. + * + * @param string $file + * + * @return string + */ + private function getExamplePathFromExampleDirectory($file) + { + return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file; + } + + /** + * Returns a path to the example file in the given directory.. + * + * @param string $directory + * @param string $file + * + * @return string + */ + private function constructExamplePath($directory, $file) + { + return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file; + } + + /** + * Get example filepath based on sourcecode. + * + * @param string $file + * + * @return string + */ + private function getExamplePathFromSource($file) + { + return sprintf( + '%s%s%s', + trim($this->getSourceDirectory(), '\\/'), + DIRECTORY_SEPARATOR, + trim($file, '"') + ); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock\Tags\Formatter; + +interface Tag +{ + public function getName(); + + public static function create($body); + + public function render(Formatter $formatter = null); + + public function __toString(); +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\Types\Context as TypeContext; + +/** + * Creates a new Description object given a body of text. + * + * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their + * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the + * Description object's `render` method. + * + * In addition to the above does a Description support two types of escape sequences: + * + * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}` + * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description + * of an inline tag. + * + * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning + * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping + * over unexpected spaces as can be observed with tag descriptions. + */ +class DescriptionFactory +{ + /** @var TagFactory */ + private $tagFactory; + + /** + * Initializes this factory with the means to construct (inline) tags. + * + * @param TagFactory $tagFactory + */ + public function __construct(TagFactory $tagFactory) + { + $this->tagFactory = $tagFactory; + } + + /** + * Returns the parsed text of this description. + * + * @param string $contents + * @param TypeContext $context + * + * @return Description + */ + public function create($contents, TypeContext $context = null) + { + list($text, $tags) = $this->parse($this->lex($contents), $context); + + return new Description($text, $tags); + } + + /** + * Strips the contents from superfluous whitespace and splits the description into a series of tokens. + * + * @param string $contents + * + * @return string[] A series of tokens of which the description text is composed. + */ + private function lex($contents) + { + $contents = $this->removeSuperfluousStartingWhitespace($contents); + + // performance optimalization; if there is no inline tag, don't bother splitting it up. + if (strpos($contents, '{@') === false) { + return [$contents]; + } + + return preg_split( + '/\{ + # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. + (?!@\}) + # We want to capture the whole tag line, but without the inline tag delimiters. + (\@ + # Match everything up to the next delimiter. + [^{}]* + # Nested inline tag content should not be captured, or it will appear in the result separately. + (?: + # Match nested inline tags. + (?: + # Because we did not catch the tag delimiters earlier, we must be explicit with them here. + # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. + \{(?1)?\} + | + # Make sure we match hanging "{". + \{ + ) + # Match content after the nested inline tag. + [^{}]* + )* # If there are more inline tags, match them as well. We use "*" since there may not be any + # nested inline tags. + ) + \}/Sux', + $contents, + null, + PREG_SPLIT_DELIM_CAPTURE + ); + } + + /** + * Parses the stream of tokens in to a new set of tokens containing Tags. + * + * @param string[] $tokens + * @param TypeContext $context + * + * @return string[]|Tag[] + */ + private function parse($tokens, TypeContext $context) + { + $count = count($tokens); + $tagCount = 0; + $tags = []; + + for ($i = 1; $i < $count; $i += 2) { + $tags[] = $this->tagFactory->create($tokens[$i], $context); + $tokens[$i] = '%' . ++$tagCount . '$s'; + } + + //In order to allow "literal" inline tags, the otherwise invalid + //sequence "{@}" is changed to "@", and "{}" is changed to "}". + //"%" is escaped to "%%" because of vsprintf. + //See unit tests for examples. + for ($i = 0; $i < $count; $i += 2) { + $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]); + } + + return [implode('', $tokens), $tags]; + } + + /** + * Removes the superfluous from a multi-line description. + * + * When a description has more than one line then it can happen that the second and subsequent lines have an + * additional indentation. This is commonly in use with tags like this: + * + * {@}since 1.1.0 This is an example + * description where we have an + * indentation in the second and + * subsequent lines. + * + * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent + * lines and this may cause rendering issues when, for example, using a Markdown converter. + * + * @param string $contents + * + * @return string + */ + private function removeSuperfluousStartingWhitespace($contents) + { + $lines = explode("\n", $contents); + + // if there is only one line then we don't have lines with superfluous whitespace and + // can use the contents as-is + if (count($lines) <= 1) { + return $contents; + } + + // determine how many whitespace characters need to be stripped + $startingSpaceCount = 9999999; + for ($i = 1; $i < count($lines); $i++) { + // lines with a no length do not count as they are not indented at all + if (strlen(trim($lines[$i])) === 0) { + continue; + } + + // determine the number of prefixing spaces by checking the difference in line length before and after + // an ltrim + $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i]))); + } + + // strip the number of spaces from each line + if ($startingSpaceCount > 0) { + for ($i = 1; $i < count($lines); $i++) { + $lines[$i] = substr($lines[$i], $startingSpaceCount); + } + } + + return implode("\n", $lines); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock\Tags\Formatter; +use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; +use Webmozart\Assert\Assert; + +/** + * Object representing to description for a DocBlock. + * + * A Description object can consist of plain text but can also include tags. A Description Formatter can then combine + * a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete + * description text using the format that you would prefer. + * + * Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is + * thus recommended to use that to create a Description object, like this: + * + * $description = $descriptionFactory->create('This is a {@see Description}', $context); + * + * The description factory will interpret the given body and create a body template and list of tags from them, and pass + * that onto the constructor if this class. + * + * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace + * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial + * > type names and FQSENs. + * + * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this: + * + * $description = new Description( + * 'This is a %1$s', + * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ] + * ); + * + * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object + * is mainly responsible for rendering. + * + * @see DescriptionFactory to create a new Description. + * @see Description\Formatter for the formatting of the body and tags. + */ +class Description +{ + /** @var string */ + private $bodyTemplate; + + /** @var Tag[] */ + private $tags; + + /** + * Initializes a Description with its body (template) and a listing of the tags used in the body template. + * + * @param string $bodyTemplate + * @param Tag[] $tags + */ + public function __construct($bodyTemplate, array $tags = []) + { + Assert::string($bodyTemplate); + + $this->bodyTemplate = $bodyTemplate; + $this->tags = $tags; + } + + /** + * Returns the tags for this DocBlock. + * + * @return Tag[] + */ + public function getTags() + { + return $this->tags; + } + + /** + * Renders this description as a string where the provided formatter will format the tags in the expected string + * format. + * + * @param Formatter|null $formatter + * + * @return string + */ + public function render(Formatter $formatter = null) + { + if ($formatter === null) { + $formatter = new PassthroughFormatter(); + } + + $tags = []; + foreach ($this->tags as $tag) { + $tags[] = '{' . $formatter->format($tag) . '}'; + } + + return vsprintf($this->bodyTemplate, $tags); + } + + /** + * Returns a plain string representation of this description. + * + * @return string + */ + public function __toString() + { + return $this->render(); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock; +use Webmozart\Assert\Assert; + +/** + * Converts a DocBlock back from an object to a complete DocComment including Asterisks. + */ +class Serializer +{ + /** @var string The string to indent the comment with. */ + protected $indentString = ' '; + + /** @var int The number of times the indent string is repeated. */ + protected $indent = 0; + + /** @var bool Whether to indent the first line with the given indent amount and string. */ + protected $isFirstLineIndented = true; + + /** @var int|null The max length of a line. */ + protected $lineLength = null; + + /** @var DocBlock\Tags\Formatter A custom tag formatter. */ + protected $tagFormatter = null; + + /** + * Create a Serializer instance. + * + * @param int $indent The number of times the indent string is repeated. + * @param string $indentString The string to indent the comment with. + * @param bool $indentFirstLine Whether to indent the first line. + * @param int|null $lineLength The max length of a line or NULL to disable line wrapping. + * @param DocBlock\Tags\Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter. + */ + public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null, $tagFormatter = null) + { + Assert::integer($indent); + Assert::string($indentString); + Assert::boolean($indentFirstLine); + Assert::nullOrInteger($lineLength); + Assert::nullOrIsInstanceOf($tagFormatter, 'phpDocumentor\Reflection\DocBlock\Tags\Formatter'); + + $this->indent = $indent; + $this->indentString = $indentString; + $this->isFirstLineIndented = $indentFirstLine; + $this->lineLength = $lineLength; + $this->tagFormatter = $tagFormatter ?: new DocBlock\Tags\Formatter\PassthroughFormatter(); + } + + /** + * Generate a DocBlock comment. + * + * @param DocBlock $docblock The DocBlock to serialize. + * + * @return string The serialized doc block. + */ + public function getDocComment(DocBlock $docblock) + { + $indent = str_repeat($this->indentString, $this->indent); + $firstIndent = $this->isFirstLineIndented ? $indent : ''; + // 3 === strlen(' * ') + $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null; + + $text = $this->removeTrailingSpaces( + $indent, + $this->addAsterisksForEachLine( + $indent, + $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength) + ) + ); + + $comment = "{$firstIndent}/**\n"; + if ($text) { + $comment .= "{$indent} * {$text}\n"; + $comment .= "{$indent} *\n"; + } + + $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment); + $comment .= $indent . ' */'; + + return $comment; + } + + /** + * @param $indent + * @param $text + * @return mixed + */ + private function removeTrailingSpaces($indent, $text) + { + return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text); + } + + /** + * @param $indent + * @param $text + * @return mixed + */ + private function addAsterisksForEachLine($indent, $text) + { + return str_replace("\n", "\n{$indent} * ", $text); + } + + /** + * @param DocBlock $docblock + * @param $wrapLength + * @return string + */ + private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength) + { + $text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription() + : ''); + if ($wrapLength !== null) { + $text = wordwrap($text, $wrapLength); + return $text; + } + + return $text; + } + + /** + * @param DocBlock $docblock + * @param $wrapLength + * @param $indent + * @param $comment + * @return string + */ + private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment) + { + foreach ($docblock->getTags() as $tag) { + $tagText = $this->tagFormatter->format($tag); + if ($wrapLength !== null) { + $tagText = wordwrap($tagText, $wrapLength); + } + + $tagText = str_replace("\n", "\n{$indent} * ", $tagText); + + $comment .= "{$indent} * {$tagText}\n"; + } + + return $comment; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\Types\Context as TypeContext; + +interface TagFactory +{ + /** + * Adds a parameter to the service locator that can be injected in a tag's factory method. + * + * When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to + * typehint a parameter in the signature so that we can use that interface or class name to inject a dependency + * (see {@see addService()} for more information on that). + * + * Another way is to check the name of the argument against the names in the Service Locator. With this method + * you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching + * name. + * + * Be aware that there are two reserved names: + * + * - name, representing the name of the tag. + * - body, representing the complete body of the tag. + * + * These parameters are injected at the last moment and will override any existing parameter with those names. + * + * @param string $name + * @param mixed $value + * + * @return void + */ + public function addParameter($name, $value); + + /** + * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided. + * + * When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter + * has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint. + * + * Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the + * interface is passed as alias then every time that interface is requested the provided service will be returned. + * + * @param object $service + * @param string $alias + * + * @return void + */ + public function addService($service); + + /** + * Factory method responsible for instantiating the correct sub type. + * + * @param string $tagLine The text for this tag, including description. + * @param TypeContext $context + * + * @throws \InvalidArgumentException if an invalid tag line was presented. + * + * @return Tag A new tag object. + */ + public function create($tagLine, TypeContext $context = null); + + /** + * Registers a handler for tags. + * + * If you want to use your own tags then you can use this method to instruct the TagFactory to register the name + * of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement the {@see Tag} interface (and thus + * the create method). + * + * @param string $tagName Name of tag to register a handler for. When registering a namespaced tag, the full + * name, along with a prefixing slash MUST be provided. + * @param string $handler FQCN of handler. + * + * @throws \InvalidArgumentException if the tag name is not a string + * @throws \InvalidArgumentException if the tag name is namespaced (contains backslashes) but does not start with + * a backslash + * @throws \InvalidArgumentException if the handler is not a string + * @throws \InvalidArgumentException if the handler is not an existing class + * @throws \InvalidArgumentException if the handler does not implement the {@see Tag} interface + * + * @return void + */ + public function registerTagHandler($tagName, $handler); +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}throws tag in a Docblock. + */ +final class Throws extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'throws'; + + /** @var Type */ + private $type; + + public function __construct(Type $type, Description $description = null) + { + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::string($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/\s+/Su', $body, 2); + + $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context); + $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context); + + return new static($type, $description); + } + + /** + * Returns the type section of the variable. + * + * @return Type + */ + public function getType() + { + return $this->type; + } + + public function __toString() + { + return $this->type . ' ' . $this->description; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Fqsen; +use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a @covers tag in a Docblock. + */ +final class Covers extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'covers'; + + /** @var Fqsen */ + private $refers = null; + + /** + * Initializes this tag. + * + * @param Fqsen $refers + * @param Description $description + */ + public function __construct(Fqsen $refers, Description $description = null) + { + $this->refers = $refers; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + DescriptionFactory $descriptionFactory = null, + FqsenResolver $resolver = null, + TypeContext $context = null + ) { + Assert::string($body); + Assert::notEmpty($body); + + $parts = preg_split('/\s+/Su', $body, 2); + + return new static( + $resolver->resolve($parts[0], $context), + $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context) + ); + } + + /** + * Returns the structural element this tag refers to. + * + * @return Fqsen + */ + public function getReference() + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + * + * @return string + */ + public function __toString() + { + return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}source tag in a Docblock. + */ +final class Source extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'source'; + + /** @var int The starting line, relative to the structural element's location. */ + private $startingLine = 1; + + /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */ + private $lineCount = null; + + public function __construct($startingLine, $lineCount = null, Description $description = null) + { + Assert::integerish($startingLine); + Assert::nullOrIntegerish($lineCount); + + $this->startingLine = (int)$startingLine; + $this->lineCount = $lineCount !== null ? (int)$lineCount : null; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) + { + Assert::stringNotEmpty($body); + Assert::notNull($descriptionFactory); + + $startingLine = 1; + $lineCount = null; + $description = null; + + // Starting line / Number of lines / Description + if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) { + $startingLine = (int)$matches[1]; + if (isset($matches[2]) && $matches[2] !== '') { + $lineCount = (int)$matches[2]; + } + + $description = $matches[3]; + } + + return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context)); + } + + /** + * Gets the starting line. + * + * @return int The starting line, relative to the structural element's + * location. + */ + public function getStartingLine() + { + return $this->startingLine; + } + + /** + * Returns the number of lines. + * + * @return int|null The number of lines, relative to the starting line. NULL + * means "to the end". + */ + public function getLineCount() + { + return $this->lineCount; + } + + public function __toString() + { + return $this->startingLine + . ($this->lineCount !== null ? ' ' . $this->lineCount : '') + . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}property-write tag in a Docblock. + */ +class PropertyWrite extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'property-write'; + + /** @var Type */ + private $type; + + /** @var string */ + protected $variableName = ''; + + /** + * @param string $variableName + * @param Type $type + * @param Description $description + */ + public function __construct($variableName, Type $type = null, Description $description = null) + { + Assert::string($variableName); + + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { + $type = $typeResolver->resolve(array_shift($parts), $context); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { + $variableName = array_shift($parts); + array_shift($parts); + + if (substr($variableName, 0, 1) === '$') { + $variableName = substr($variableName, 1); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Returns the variable's type or null if unknown. + * + * @return Type|null + */ + public function getType() + { + return $this->type; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return ($this->type ? $this->type . ' ' : '') + . '$' . $this->variableName + . ($this->description ? ' ' . $this->description : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}return tag in a Docblock. + */ +final class Return_ extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'return'; + + /** @var Type */ + private $type; + + public function __construct(Type $type, Description $description = null) + { + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::string($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/\s+/Su', $body, 2); + + $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context); + $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context); + + return new static($type, $description); + } + + /** + * Returns the type section of the variable. + * + * @return Type + */ + public function getType() + { + return $this->type; + } + + public function __toString() + { + return $this->type . ' ' . $this->description; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\DocBlock\StandardTagFactory; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Parses a tag definition for a DocBlock. + */ +class Generic extends BaseTag implements Factory\StaticMethod +{ + /** + * Parses a tag and populates the member variables. + * + * @param string $name Name of the tag. + * @param Description $description The contents of the given tag. + */ + public function __construct($name, Description $description = null) + { + $this->validateTagName($name); + + $this->name = $name; + $this->description = $description; + } + + /** + * Creates a new tag that represents any unknown tag type. + * + * @param string $body + * @param string $name + * @param DescriptionFactory $descriptionFactory + * @param TypeContext $context + * + * @return static + */ + public static function create( + $body, + $name = '', + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::string($body); + Assert::stringNotEmpty($name); + Assert::notNull($descriptionFactory); + + $description = $descriptionFactory && $body ? $descriptionFactory->create($body, $context) : null; + + return new static($name, $description); + } + + /** + * Returns the tag as a serialized string + * + * @return string + */ + public function __toString() + { + return ($this->description ? $this->description->render() : ''); + } + + /** + * Validates if the tag name matches the expected format, otherwise throws an exception. + * + * @param string $name + * + * @return void + */ + private function validateTagName($name) + { + if (! preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) { + throw new \InvalidArgumentException( + 'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' + . 'hyphens and backslashes.' + ); + } + } +} + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}version tag in a Docblock. + */ +final class Version extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'version'; + + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + const REGEX_VECTOR = '(?: + # Normal release vectors. + \d\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\s\:]+\:\s*\$[^\$]+\$ + )'; + + /** @var string The version vector. */ + private $version = ''; + + public function __construct($version = null, Description $description = null) + { + Assert::nullOrStringNotEmpty($version); + + $this->version = $version; + $this->description = $description; + } + + /** + * @return static + */ + public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) + { + Assert::nullOrString($body); + if (empty($body)) { + return new static(); + } + + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { + return null; + } + + return new static( + $matches[1], + $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) + ); + } + + /** + * Gets the version section of the tag. + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->version . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}deprecated tag in a Docblock. + */ +final class Deprecated extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'deprecated'; + + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + const REGEX_VECTOR = '(?: + # Normal release vectors. + \d\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\s\:]+\:\s*\$[^\$]+\$ + )'; + + /** @var string The version vector. */ + private $version = ''; + + public function __construct($version = null, Description $description = null) + { + Assert::nullOrStringNotEmpty($version); + + $this->version = $version; + $this->description = $description; + } + + /** + * @return static + */ + public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) + { + Assert::nullOrString($body); + if (empty($body)) { + return new static(); + } + + $matches = []; + if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { + return new static( + null, + null !== $descriptionFactory ? $descriptionFactory->create($body, $context) : null + ); + } + + return new static( + $matches[1], + $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) + ); + } + + /** + * Gets the version section of the tag. + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->version . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; + +interface Strategy +{ + public function create($body); +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; + +interface StaticMethod +{ + public static function create($body); +} + + * @copyright 2017 Mike van Riel + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter; + +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Tags\Formatter; + +class AlignFormatter implements Formatter +{ + /** @var int The maximum tag name length. */ + protected $maxLen = 0; + + /** + * Constructor. + * + * @param Tag[] $tags All tags that should later be aligned with the formatter. + */ + public function __construct(array $tags) + { + foreach ($tags as $tag) { + $this->maxLen = max($this->maxLen, strlen($tag->getName())); + } + } + + /** + * Formats the given tag to return a simple plain text version. + * + * @param Tag $tag + * + * @return string + */ + public function format(Tag $tag) + { + return '@' . $tag->getName() . str_repeat(' ', $this->maxLen - strlen($tag->getName()) + 1) . (string)$tag; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter; + +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Tags\Formatter; + +class PassthroughFormatter implements Formatter +{ + /** + * Formats the given tag to return a simple plain text version. + * + * @param Tag $tag + * + * @return string + */ + public function format(Tag $tag) + { + return trim('@' . $tag->getName() . ' ' . (string)$tag); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Description; + +/** + * Parses a tag definition for a DocBlock. + */ +abstract class BaseTag implements DocBlock\Tag +{ + /** @var string Name of the tag */ + protected $name = ''; + + /** @var Description|null Description of the tag. */ + protected $description; + + /** + * Gets the name of this tag. + * + * @return string The name of this tag. + */ + public function getName() + { + return $this->name; + } + + public function getDescription() + { + return $this->description; + } + + public function render(Formatter $formatter = null) + { + if ($formatter === null) { + $formatter = new Formatter\PassthroughFormatter(); + } + + return $formatter->format($this); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\Tag; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}example tag in a Docblock. + */ +final class Example extends BaseTag +{ + /** + * @var string Path to a file to use as an example. May also be an absolute URI. + */ + private $filePath; + + /** + * @var bool Whether the file path component represents an URI. This determines how the file portion + * appears at {@link getContent()}. + */ + private $isURI = false; + + /** + * @var int + */ + private $startingLine; + + /** + * @var int + */ + private $lineCount; + + public function __construct($filePath, $isURI, $startingLine, $lineCount, $description) + { + Assert::notEmpty($filePath); + Assert::integer($startingLine); + Assert::greaterThanEq($startingLine, 0); + + $this->filePath = $filePath; + $this->startingLine = $startingLine; + $this->lineCount = $lineCount; + $this->name = 'example'; + if ($description !== null) { + $this->description = trim($description); + } + + $this->isURI = $isURI; + } + + /** + * {@inheritdoc} + */ + public function getContent() + { + if (null === $this->description) { + $filePath = '"' . $this->filePath . '"'; + if ($this->isURI) { + $filePath = $this->isUriRelative($this->filePath) + ? str_replace('%2F', '/', rawurlencode($this->filePath)) + :$this->filePath; + } + + return trim($filePath . ' ' . parent::getDescription()); + } + + return $this->description; + } + + /** + * {@inheritdoc} + */ + public static function create($body) + { + // File component: File path in quotes or File URI / Source information + if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) { + return null; + } + + $filePath = null; + $fileUri = null; + if ('' !== $matches[1]) { + $filePath = $matches[1]; + } else { + $fileUri = $matches[2]; + } + + $startingLine = 1; + $lineCount = null; + $description = null; + + if (array_key_exists(3, $matches)) { + $description = $matches[3]; + + // Starting line / Number of lines / Description + if (preg_match('/^([1-9]\d*)(?:\s+((?1))\s*)?(.*)$/sux', $matches[3], $contentMatches)) { + $startingLine = (int)$contentMatches[1]; + if (isset($contentMatches[2]) && $contentMatches[2] !== '') { + $lineCount = (int)$contentMatches[2]; + } + + if (array_key_exists(3, $contentMatches)) { + $description = $contentMatches[3]; + } + } + } + + return new static( + $filePath !== null?$filePath:$fileUri, + $fileUri !== null, + $startingLine, + $lineCount, + $description + ); + } + + /** + * Returns the file path. + * + * @return string Path to a file to use as an example. + * May also be an absolute URI. + */ + public function getFilePath() + { + return $this->filePath; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->filePath . ($this->description ? ' ' . $this->description : ''); + } + + /** + * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). + * + * @param string $uri + * + * @return bool + */ + private function isUriRelative($uri) + { + return false === strpos($uri, ':'); + } + + /** + * @return int + */ + public function getStartingLine() + { + return $this->startingLine; + } + + /** + * @return int + */ + public function getLineCount() + { + return $this->lineCount; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}property tag in a Docblock. + */ +class Property extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'property'; + + /** @var Type */ + private $type; + + /** @var string */ + protected $variableName = ''; + + /** + * @param string $variableName + * @param Type $type + * @param Description $description + */ + public function __construct($variableName, Type $type = null, Description $description = null) + { + Assert::string($variableName); + + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { + $type = $typeResolver->resolve(array_shift($parts), $context); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { + $variableName = array_shift($parts); + array_shift($parts); + + if (substr($variableName, 0, 1) === '$') { + $variableName = substr($variableName, 1); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Returns the variable's type or null if unknown. + * + * @return Type|null + */ + public function getType() + { + return $this->type; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return ($this->type ? $this->type . ' ' : '') + . '$' . $this->variableName + . ($this->description ? ' ' . $this->description : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef; +use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference; +use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url; +use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for an {@}see tag in a Docblock. + */ +class See extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'see'; + + /** @var Reference */ + protected $refers = null; + + /** + * Initializes this tag. + * + * @param Reference $refers + * @param Description $description + */ + public function __construct(Reference $refers, Description $description = null) + { + $this->refers = $refers; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + FqsenResolver $resolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::string($body); + Assert::allNotNull([$resolver, $descriptionFactory]); + + $parts = preg_split('/\s+/Su', $body, 2); + $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; + + // https://tools.ietf.org/html/rfc2396#section-3 + if (preg_match('/\w:\/\/\w/i', $parts[0])) { + return new static(new Url($parts[0]), $description); + } + + return new static(new FqsenRef($resolver->resolve($parts[0], $context)), $description); + } + + /** + * Returns the ref of this tag. + * + * @return Reference + */ + public function getReference() + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + * + * @return string + */ + public function __toString() + { + return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}property-read tag in a Docblock. + */ +class PropertyRead extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'property-read'; + + /** @var Type */ + private $type; + + /** @var string */ + protected $variableName = ''; + + /** + * @param string $variableName + * @param Type $type + * @param Description $description + */ + public function __construct($variableName, Type $type = null, Description $description = null) + { + Assert::string($variableName); + + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { + $type = $typeResolver->resolve(array_shift($parts), $context); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { + $variableName = array_shift($parts); + array_shift($parts); + + if (substr($variableName, 0, 1) === '$') { + $variableName = substr($variableName, 1); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Returns the variable's type or null if unknown. + * + * @return Type|null + */ + public function getType() + { + return $this->type; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return ($this->type ? $this->type . ' ' : '') + . '$' . $this->variableName + . ($this->description ? ' ' . $this->description : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use Webmozart\Assert\Assert; + +/** + * Reflection class for an {@}author tag in a Docblock. + */ +final class Author extends BaseTag implements Factory\StaticMethod +{ + /** @var string register that this is the author tag. */ + protected $name = 'author'; + + /** @var string The name of the author */ + private $authorName = ''; + + /** @var string The email of the author */ + private $authorEmail = ''; + + /** + * Initializes this tag with the author name and e-mail. + * + * @param string $authorName + * @param string $authorEmail + */ + public function __construct($authorName, $authorEmail) + { + Assert::string($authorName); + Assert::string($authorEmail); + if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) { + throw new \InvalidArgumentException('The author tag does not have a valid e-mail address'); + } + + $this->authorName = $authorName; + $this->authorEmail = $authorEmail; + } + + /** + * Gets the author's name. + * + * @return string The author's name. + */ + public function getAuthorName() + { + return $this->authorName; + } + + /** + * Returns the author's email. + * + * @return string The author's email. + */ + public function getEmail() + { + return $this->authorEmail; + } + + /** + * Returns this tag in string form. + * + * @return string + */ + public function __toString() + { + return $this->authorName . (strlen($this->authorEmail) ? ' <' . $this->authorEmail . '>' : ''); + } + + /** + * Attempts to create a new Author object based on †he tag body. + * + * @param string $body + * + * @return static + */ + public static function create($body) + { + Assert::string($body); + + $splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches); + if (!$splitTagContent) { + return null; + } + + $authorName = trim($matches[1]); + $email = isset($matches[2]) ? trim($matches[2]) : ''; + + return new static($authorName, $email); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}since tag in a Docblock. + */ +final class Since extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'since'; + + /** + * PCRE regular expression matching a version vector. + * Assumes the "x" modifier. + */ + const REGEX_VECTOR = '(?: + # Normal release vectors. + \d\S* + | + # VCS version vectors. Per PHPCS, they are expected to + # follow the form of the VCS name, followed by ":", followed + # by the version vector itself. + # By convention, popular VCSes like CVS, SVN and GIT use "$" + # around the actual version vector. + [^\s\:]+\:\s*\$[^\$]+\$ + )'; + + /** @var string The version vector. */ + private $version = ''; + + public function __construct($version = null, Description $description = null) + { + Assert::nullOrStringNotEmpty($version); + + $this->version = $version; + $this->description = $description; + } + + /** + * @return static + */ + public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) + { + Assert::nullOrString($body); + if (empty($body)) { + return new static(); + } + + $matches = []; + if (! preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { + return null; + } + + return new static( + $matches[1], + $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) + ); + } + + /** + * Gets the version section of the tag. + * + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->version . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a @link tag in a Docblock. + */ +final class Link extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'link'; + + /** @var string */ + private $link = ''; + + /** + * Initializes a link to a URL. + * + * @param string $link + * @param Description $description + */ + public function __construct($link, Description $description = null) + { + Assert::string($link); + + $this->link = $link; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) + { + Assert::string($body); + Assert::notNull($descriptionFactory); + + $parts = preg_split('/\s+/Su', $body, 2); + $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; + + return new static($parts[0], $description); + } + + /** + * Gets the link + * + * @return string + */ + public function getLink() + { + return $this->link; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return $this->link . ($this->description ? ' ' . $this->description->render() : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use phpDocumentor\Reflection\Types\Void_; +use Webmozart\Assert\Assert; + +/** + * Reflection class for an {@}method in a Docblock. + */ +final class Method extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'method'; + + /** @var string */ + private $methodName = ''; + + /** @var string[] */ + private $arguments = []; + + /** @var bool */ + private $isStatic = false; + + /** @var Type */ + private $returnType; + + public function __construct( + $methodName, + array $arguments = [], + Type $returnType = null, + $static = false, + Description $description = null + ) { + Assert::stringNotEmpty($methodName); + Assert::boolean($static); + + if ($returnType === null) { + $returnType = new Void_(); + } + + $this->methodName = $methodName; + $this->arguments = $this->filterArguments($arguments); + $this->returnType = $returnType; + $this->isStatic = $static; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([ $typeResolver, $descriptionFactory ]); + + // 1. none or more whitespace + // 2. optionally the keyword "static" followed by whitespace + // 3. optionally a word with underscores followed by whitespace : as + // type for the return value + // 4. then optionally a word with underscores followed by () and + // whitespace : as method name as used by phpDocumentor + // 5. then a word with underscores, followed by ( and any character + // until a ) and whitespace : as method name with signature + // 6. any remaining text : as description + if (!preg_match( + '/^ + # Static keyword + # Declares a static method ONLY if type is also present + (?: + (static) + \s+ + )? + # Return type + (?: + ( + (?:[\w\|_\\\\]*\$this[\w\|_\\\\]*) + | + (?: + (?:[\w\|_\\\\]+) + # array notation + (?:\[\])* + )* + ) + \s+ + )? + # Legacy method name (not captured) + (?: + [\w_]+\(\)\s+ + )? + # Method name + ([\w\|_\\\\]+) + # Arguments + (?: + \(([^\)]*)\) + )? + \s* + # Description + (.*) + $/sux', + $body, + $matches + )) { + return null; + } + + list(, $static, $returnType, $methodName, $arguments, $description) = $matches; + + $static = $static === 'static'; + + if ($returnType === '') { + $returnType = 'void'; + } + + $returnType = $typeResolver->resolve($returnType, $context); + $description = $descriptionFactory->create($description, $context); + + if (is_string($arguments) && strlen($arguments) > 0) { + $arguments = explode(',', $arguments); + foreach ($arguments as &$argument) { + $argument = explode(' ', self::stripRestArg(trim($argument)), 2); + if ($argument[0][0] === '$') { + $argumentName = substr($argument[0], 1); + $argumentType = new Void_(); + } else { + $argumentType = $typeResolver->resolve($argument[0], $context); + $argumentName = ''; + if (isset($argument[1])) { + $argument[1] = self::stripRestArg($argument[1]); + $argumentName = substr($argument[1], 1); + } + } + + $argument = [ 'name' => $argumentName, 'type' => $argumentType]; + } + } else { + $arguments = []; + } + + return new static($methodName, $arguments, $returnType, $static, $description); + } + + /** + * Retrieves the method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * @return string[] + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Checks whether the method tag describes a static method or not. + * + * @return bool TRUE if the method declaration is for a static method, FALSE otherwise. + */ + public function isStatic() + { + return $this->isStatic; + } + + /** + * @return Type + */ + public function getReturnType() + { + return $this->returnType; + } + + public function __toString() + { + $arguments = []; + foreach ($this->arguments as $argument) { + $arguments[] = $argument['type'] . ' $' . $argument['name']; + } + + return trim(($this->isStatic() ? 'static ' : '') + . (string)$this->returnType . ' ' + . $this->methodName + . '(' . implode(', ', $arguments) . ')' + . ($this->description ? ' ' . $this->description->render() : '')); + } + + private function filterArguments($arguments) + { + foreach ($arguments as &$argument) { + if (is_string($argument)) { + $argument = [ 'name' => $argument ]; + } + + if (! isset($argument['type'])) { + $argument['type'] = new Void_(); + } + + $keys = array_keys($argument); + sort($keys); + if ($keys !== [ 'name', 'type' ]) { + throw new \InvalidArgumentException( + 'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true) + ); + } + } + + return $arguments; + } + + private static function stripRestArg($argument) + { + if (strpos($argument, '...') === 0) { + $argument = trim(substr($argument, 3)); + } + + return $argument; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Tag; + +interface Formatter +{ + /** + * Formats a tag into a string representation according to a specific format, such as Markdown. + * + * @param Tag $tag + * + * @return string + */ + public function format(Tag $tag); +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for the {@}param tag in a Docblock. + */ +final class Param extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'param'; + + /** @var Type */ + private $type; + + /** @var string */ + private $variableName = ''; + + /** @var bool determines whether this is a variadic argument */ + private $isVariadic = false; + + /** + * @param string $variableName + * @param Type $type + * @param bool $isVariadic + * @param Description $description + */ + public function __construct($variableName, Type $type = null, $isVariadic = false, Description $description = null) + { + Assert::string($variableName); + Assert::boolean($isVariadic); + + $this->variableName = $variableName; + $this->type = $type; + $this->isVariadic = $isVariadic; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + $isVariadic = false; + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { + $type = $typeResolver->resolve(array_shift($parts), $context); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$' || substr($parts[0], 0, 4) === '...$')) { + $variableName = array_shift($parts); + array_shift($parts); + + if (substr($variableName, 0, 3) === '...') { + $isVariadic = true; + $variableName = substr($variableName, 3); + } + + if (substr($variableName, 0, 1) === '$') { + $variableName = substr($variableName, 1); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $isVariadic, $description); + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Returns the variable's type or null if unknown. + * + * @return Type|null + */ + public function getType() + { + return $this->type; + } + + /** + * Returns whether this tag is variadic. + * + * @return boolean + */ + public function isVariadic() + { + return $this->isVariadic; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return ($this->type ? $this->type . ' ' : '') + . ($this->isVariadic() ? '...' : '') + . '$' . $this->variableName + . ($this->description ? ' ' . $this->description : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Type; +use phpDocumentor\Reflection\TypeResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}var tag in a Docblock. + */ +class Var_ extends BaseTag implements Factory\StaticMethod +{ + /** @var string */ + protected $name = 'var'; + + /** @var Type */ + private $type; + + /** @var string */ + protected $variableName = ''; + + /** + * @param string $variableName + * @param Type $type + * @param Description $description + */ + public function __construct($variableName, Type $type = null, Description $description = null) + { + Assert::string($variableName); + + $this->variableName = $variableName; + $this->type = $type; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + TypeResolver $typeResolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::stringNotEmpty($body); + Assert::allNotNull([$typeResolver, $descriptionFactory]); + + $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); + $type = null; + $variableName = ''; + + // if the first item that is encountered is not a variable; it is a type + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { + $type = $typeResolver->resolve(array_shift($parts), $context); + array_shift($parts); + } + + // if the next item starts with a $ or ...$ it must be the variable name + if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { + $variableName = array_shift($parts); + array_shift($parts); + + if (substr($variableName, 0, 1) === '$') { + $variableName = substr($variableName, 1); + } + } + + $description = $descriptionFactory->create(implode('', $parts), $context); + + return new static($variableName, $type, $description); + } + + /** + * Returns the variable's name. + * + * @return string + */ + public function getVariableName() + { + return $this->variableName; + } + + /** + * Returns the variable's type or null if unknown. + * + * @return Type|null + */ + public function getType() + { + return $this->type; + } + + /** + * Returns a string representation for this tag. + * + * @return string + */ + public function __toString() + { + return ($this->type ? $this->type . ' ' : '') + . (empty($this->variableName) ? null : ('$' . $this->variableName)) + . ($this->description ? ' ' . $this->description : ''); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; + +use Webmozart\Assert\Assert; + +/** + * Url reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See} + */ +final class Url implements Reference +{ + /** + * @var string + */ + private $uri; + + /** + * Url constructor. + */ + public function __construct($uri) + { + Assert::stringNotEmpty($uri); + $this->uri = $uri; + } + + public function __toString() + { + return $this->uri; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; + +use phpDocumentor\Reflection\Fqsen as RealFqsen; + +/** + * Fqsen reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See} + */ +final class Fqsen implements Reference +{ + /** + * @var RealFqsen + */ + private $fqsen; + + /** + * Fqsen constructor. + */ + public function __construct(RealFqsen $fqsen) + { + $this->fqsen = $fqsen; + } + + /** + * @return string string representation of the referenced fqsen + */ + public function __toString() + { + return (string)$this->fqsen; + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; + +/** + * Interface for references in {@see phpDocumentor\Reflection\DocBlock\Tags\See} + */ +interface Reference +{ + public function __toString(); +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\Fqsen; +use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Reflection class for a {@}uses tag in a Docblock. + */ +final class Uses extends BaseTag implements Factory\StaticMethod +{ + protected $name = 'uses'; + + /** @var Fqsen */ + protected $refers = null; + + /** + * Initializes this tag. + * + * @param Fqsen $refers + * @param Description $description + */ + public function __construct(Fqsen $refers, Description $description = null) + { + $this->refers = $refers; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public static function create( + $body, + FqsenResolver $resolver = null, + DescriptionFactory $descriptionFactory = null, + TypeContext $context = null + ) { + Assert::string($body); + Assert::allNotNull([$resolver, $descriptionFactory]); + + $parts = preg_split('/\s+/Su', $body, 2); + + return new static( + $resolver->resolve($parts[0], $context), + $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context) + ); + } + + /** + * Returns the structural element this tag refers to. + * + * @return Fqsen + */ + public function getReference() + { + return $this->refers; + } + + /** + * Returns a string representation of this tag. + * + * @return string + */ + public function __toString() + { + return $this->refers . ' ' . $this->description->render(); + } +} + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock; + +use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod; +use phpDocumentor\Reflection\DocBlock\Tags\Generic; +use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\Types\Context as TypeContext; +use Webmozart\Assert\Assert; + +/** + * Creates a Tag object given the contents of a tag. + * + * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create` + * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can + * pass the dependencies that you need to construct a tag object. + * + * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise + * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to + * > verify that a dependency is actually passed. + * + * This Factory also features a Service Locator component that is used to pass the right dependencies to the + * `create` method of a tag; each dependency should be registered as a service or as a parameter. + * + * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass + * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface. + */ +final class StandardTagFactory implements TagFactory +{ + /** PCRE regular expression matching a tag name. */ + const REGEX_TAGNAME = '[\w\-\_\\\\]+'; + + /** + * @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value. + */ + private $tagHandlerMappings = [ + 'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author', + 'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers', + 'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated', + // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', + 'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link', + 'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method', + 'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param', + 'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead', + 'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property', + 'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite', + 'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return_', + 'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See', + 'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since', + 'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source', + 'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', + 'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', + 'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses', + 'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_', + 'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version' + ]; + + /** + * @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used. + */ + private $tagHandlerParameterCache = []; + + /** + * @var FqsenResolver + */ + private $fqsenResolver; + + /** + * @var mixed[] an array representing a simple Service Locator where we can store parameters and + * services that can be inserted into the Factory Methods of Tag Handlers. + */ + private $serviceLocator = []; + + /** + * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers. + * + * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property + * is used. + * + * @param FqsenResolver $fqsenResolver + * @param string[] $tagHandlers + * + * @see self::registerTagHandler() to add a new tag handler to the existing default list. + */ + public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null) + { + $this->fqsenResolver = $fqsenResolver; + if ($tagHandlers !== null) { + $this->tagHandlerMappings = $tagHandlers; + } + + $this->addService($fqsenResolver, FqsenResolver::class); + } + + /** + * {@inheritDoc} + */ + public function create($tagLine, TypeContext $context = null) + { + if (! $context) { + $context = new TypeContext(''); + } + + list($tagName, $tagBody) = $this->extractTagParts($tagLine); + + if ($tagBody !== '' && $tagBody[0] === '[') { + throw new \InvalidArgumentException( + 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' + ); + } + + return $this->createTag($tagBody, $tagName, $context); + } + + /** + * {@inheritDoc} + */ + public function addParameter($name, $value) + { + $this->serviceLocator[$name] = $value; + } + + /** + * {@inheritDoc} + */ + public function addService($service, $alias = null) + { + $this->serviceLocator[$alias ?: get_class($service)] = $service; + } + + /** + * {@inheritDoc} + */ + public function registerTagHandler($tagName, $handler) + { + Assert::stringNotEmpty($tagName); + Assert::stringNotEmpty($handler); + Assert::classExists($handler); + Assert::implementsInterface($handler, StaticMethod::class); + + if (strpos($tagName, '\\') && $tagName[0] !== '\\') { + throw new \InvalidArgumentException( + 'A namespaced tag must have a leading backslash as it must be fully qualified' + ); + } + + $this->tagHandlerMappings[$tagName] = $handler; + } + + /** + * Extracts all components for a tag. + * + * @param string $tagLine + * + * @return string[] + */ + private function extractTagParts($tagLine) + { + $matches = []; + if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)/us', $tagLine, $matches)) { + throw new \InvalidArgumentException( + 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' + ); + } + + if (count($matches) < 3) { + $matches[] = ''; + } + + return array_slice($matches, 1); + } + + /** + * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the + * body was invalid. + * + * @param string $body + * @param string $name + * @param TypeContext $context + * + * @return Tag|null + */ + private function createTag($body, $name, TypeContext $context) + { + $handlerClassName = $this->findHandlerClassName($name, $context); + $arguments = $this->getArgumentsForParametersFromWiring( + $this->fetchParametersForHandlerFactoryMethod($handlerClassName), + $this->getServiceLocatorWithDynamicParameters($context, $name, $body) + ); + + return call_user_func_array([$handlerClassName, 'create'], $arguments); + } + + /** + * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`). + * + * @param string $tagName + * @param TypeContext $context + * + * @return string + */ + private function findHandlerClassName($tagName, TypeContext $context) + { + $handlerClassName = Generic::class; + if (isset($this->tagHandlerMappings[$tagName])) { + $handlerClassName = $this->tagHandlerMappings[$tagName]; + } elseif ($this->isAnnotation($tagName)) { + // TODO: Annotation support is planned for a later stage and as such is disabled for now + // $tagName = (string)$this->fqsenResolver->resolve($tagName, $context); + // if (isset($this->annotationMappings[$tagName])) { + // $handlerClassName = $this->annotationMappings[$tagName]; + // } + } + + return $handlerClassName; + } + + /** + * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters. + * + * @param \ReflectionParameter[] $parameters + * @param mixed[] $locator + * + * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters + * is provided with this method. + */ + private function getArgumentsForParametersFromWiring($parameters, $locator) + { + $arguments = []; + foreach ($parameters as $index => $parameter) { + $typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null; + if (isset($locator[$typeHint])) { + $arguments[] = $locator[$typeHint]; + continue; + } + + $parameterName = $parameter->getName(); + if (isset($locator[$parameterName])) { + $arguments[] = $locator[$parameterName]; + continue; + } + + $arguments[] = null; + } + + return $arguments; + } + + /** + * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given + * tag handler class name. + * + * @param string $handlerClassName + * + * @return \ReflectionParameter[] + */ + private function fetchParametersForHandlerFactoryMethod($handlerClassName) + { + if (! isset($this->tagHandlerParameterCache[$handlerClassName])) { + $methodReflection = new \ReflectionMethod($handlerClassName, 'create'); + $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters(); + } + + return $this->tagHandlerParameterCache[$handlerClassName]; + } + + /** + * Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and + * Context. + * + * @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs. + * @param string $tagName The name of the tag that may be passed onto the factory method of the Tag class. + * @param string $tagBody The body of the tag that may be passed onto the factory method of the Tag class. + * + * @return mixed[] + */ + private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody) + { + $locator = array_merge( + $this->serviceLocator, + [ + 'name' => $tagName, + 'body' => $tagBody, + TypeContext::class => $context + ] + ); + + return $locator; + } + + /** + * Returns whether the given tag belongs to an annotation. + * + * @param string $tagContent + * + * @todo this method should be populated once we implement Annotation notation support. + * + * @return bool + */ + private function isAnnotation($tagContent) + { + // 1. Contains a namespace separator + // 2. Contains parenthesis + // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part + // of the annotation class name matches the found tag name + + return false; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ComponentElementCollection extends ElementCollection { + public function current() { + return new ComponentElement( + $this->getCurrentElement() + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use LibXMLError; + +class ManifestDocumentLoadingException extends \Exception implements Exception { + /** + * @var LibXMLError[] + */ + private $libxmlErrors; + + /** + * ManifestDocumentLoadingException constructor. + * + * @param LibXMLError[] $libxmlErrors + */ + public function __construct(array $libxmlErrors) { + $this->libxmlErrors = $libxmlErrors; + $first = $this->libxmlErrors[0]; + + parent::__construct( + sprintf( + '%s (Line: %d / Column: %d / File: %s)', + $first->message, + $first->line, + $first->column, + $first->file + ), + $first->code + ); + } + + /** + * @return LibXMLError[] + */ + public function getLibxmlErrors() { + return $this->libxmlErrors; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class LicenseElement extends ManifestElement { + public function getType() { + return $this->getAttributeValue('type'); + } + + public function getUrl() { + return $this->getAttributeValue('url'); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class RequiresElement extends ManifestElement { + public function getPHPElement() { + return new PhpElement( + $this->getChildByName('php') + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class CopyrightElement extends ManifestElement { + public function getAuthorElements() { + return new AuthorElementCollection( + $this->getChildrenByName('author') + ); + } + + public function getLicenseElement() { + return new LicenseElement( + $this->getChildByName('license') + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ExtElementCollection extends ElementCollection { + public function current() { + return new ExtElement( + $this->getCurrentElement() + ); + } + +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ExtensionElement extends ManifestElement { + public function getFor() { + return $this->getAttributeValue('for'); + } + + public function getCompatible() { + return $this->getAttributeValue('compatible'); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class AuthorElement extends ManifestElement { + public function getName() { + return $this->getAttributeValue('name'); + } + + public function getEmail() { + return $this->getAttributeValue('email'); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class BundlesElement extends ManifestElement { + public function getComponentElements() { + return new ComponentElementCollection( + $this->getChildrenByName('component') + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ComponentElement extends ManifestElement { + public function getName() { + return $this->getAttributeValue('name'); + } + + public function getVersion() { + return $this->getAttributeValue('version'); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use DOMElement; +use DOMNodeList; + +abstract class ElementCollection implements \Iterator { + /** + * @var DOMNodeList + */ + private $nodeList; + + private $position; + + /** + * ElementCollection constructor. + * + * @param DOMNodeList $nodeList + */ + public function __construct(DOMNodeList $nodeList) { + $this->nodeList = $nodeList; + $this->position = 0; + } + + abstract public function current(); + + /** + * @return DOMElement + */ + protected function getCurrentElement() { + return $this->nodeList->item($this->position); + } + + public function next() { + $this->position++; + } + + public function key() { + return $this->position; + } + + public function valid() { + return $this->position < $this->nodeList->length; + } + + public function rewind() { + $this->position = 0; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class AuthorElementCollection extends ElementCollection { + public function current() { + return new AuthorElement( + $this->getCurrentElement() + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class PhpElement extends ManifestElement { + public function getVersion() { + return $this->getAttributeValue('version'); + } + + public function hasExtElements() { + return $this->hasChild('ext'); + } + + public function getExtElements() { + return new ExtElementCollection( + $this->getChildrenByName('ext') + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ExtElement extends ManifestElement { + public function getName() { + return $this->getAttributeValue('name'); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use DOMElement; +use DOMNodeList; + +class ManifestElement { + const XMLNS = 'https://phar.io/xml/manifest/1.0'; + + /** + * @var DOMElement + */ + private $element; + + /** + * ContainsElement constructor. + * + * @param DOMElement $element + */ + public function __construct(DOMElement $element) { + $this->element = $element; + } + + /** + * @param string $name + * + * @return string + * + * @throws ManifestElementException + */ + protected function getAttributeValue($name) { + if (!$this->element->hasAttribute($name)) { + throw new ManifestElementException( + sprintf( + 'Attribute %s not set on element %s', + $name, + $this->element->localName + ) + ); + } + + return $this->element->getAttribute($name); + } + + /** + * @param $elementName + * + * @return DOMElement + * + * @throws ManifestElementException + */ + protected function getChildByName($elementName) { + $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); + + if (!$element instanceof DOMElement) { + throw new ManifestElementException( + sprintf('Element %s missing', $elementName) + ); + } + + return $element; + } + + /** + * @param $elementName + * + * @return DOMNodeList + * + * @throws ManifestElementException + */ + protected function getChildrenByName($elementName) { + $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); + + if ($elementList->length === 0) { + throw new ManifestElementException( + sprintf('Element(s) %s missing', $elementName) + ); + } + + return $elementList; + } + + /** + * @param string $elementName + * + * @return bool + */ + protected function hasChild($elementName) { + return $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->length !== 0; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use DOMDocument; +use DOMElement; + +class ManifestDocument { + const XMLNS = 'https://phar.io/xml/manifest/1.0'; + + /** + * @var DOMDocument + */ + private $dom; + + /** + * ManifestDocument constructor. + * + * @param DOMDocument $dom + */ + private function __construct(DOMDocument $dom) { + $this->ensureCorrectDocumentType($dom); + + $this->dom = $dom; + } + + public static function fromFile($filename) { + if (!file_exists($filename)) { + throw new ManifestDocumentException( + sprintf('File "%s" not found', $filename) + ); + } + + return self::fromString( + file_get_contents($filename) + ); + } + + public static function fromString($xmlString) { + $prev = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $dom = new DOMDocument(); + $dom->loadXML($xmlString); + + $errors = libxml_get_errors(); + libxml_use_internal_errors($prev); + + if (count($errors) !== 0) { + throw new ManifestDocumentLoadingException($errors); + } + + return new self($dom); + } + + public function getContainsElement() { + return new ContainsElement( + $this->fetchElementByName('contains') + ); + } + + public function getCopyrightElement() { + return new CopyrightElement( + $this->fetchElementByName('copyright') + ); + } + + public function getRequiresElement() { + return new RequiresElement( + $this->fetchElementByName('requires') + ); + } + + public function hasBundlesElement() { + return $this->dom->getElementsByTagNameNS(self::XMLNS, 'bundles')->length === 1; + } + + public function getBundlesElement() { + return new BundlesElement( + $this->fetchElementByName('bundles') + ); + } + + private function ensureCorrectDocumentType(DOMDocument $dom) { + $root = $dom->documentElement; + + if ($root->localName !== 'phar' || $root->namespaceURI !== self::XMLNS) { + throw new ManifestDocumentException('Not a phar.io manifest document'); + } + } + + /** + * @param $elementName + * + * @return DOMElement + * + * @throws ManifestDocumentException + */ + private function fetchElementByName($elementName) { + $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); + + if (!$element instanceof DOMElement) { + throw new ManifestDocumentException( + sprintf('Element %s missing', $elementName) + ); + } + + return $element; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ContainsElement extends ManifestElement { + public function getName() { + return $this->getAttributeValue('name'); + } + + public function getVersion() { + return $this->getAttributeValue('version'); + } + + public function getType() { + return $this->getAttributeValue('type'); + } + + public function getExtensionElement() { + return new ExtensionElement( + $this->getChildByName('extension') + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\AnyVersionConstraint; +use PharIo\Version\Version; +use PharIo\Version\VersionConstraint; +use XMLWriter; + +class ManifestSerializer { + /** + * @var XMLWriter + */ + private $xmlWriter; + + public function serializeToFile(Manifest $manifest, $filename) { + file_put_contents( + $filename, + $this->serializeToString($manifest) + ); + } + + public function serializeToString(Manifest $manifest) { + $this->startDocument(); + + $this->addContains($manifest->getName(), $manifest->getVersion(), $manifest->getType()); + $this->addCopyright($manifest->getCopyrightInformation()); + $this->addRequirements($manifest->getRequirements()); + $this->addBundles($manifest->getBundledComponents()); + + return $this->finishDocument(); + } + + private function startDocument() { + $xmlWriter = new XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->setIndent(true); + $xmlWriter->setIndentString(str_repeat(' ', 4)); + $xmlWriter->startDocument('1.0', 'UTF-8'); + $xmlWriter->startElement('phar'); + $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); + + $this->xmlWriter = $xmlWriter; + } + + private function finishDocument() { + $this->xmlWriter->endElement(); + $this->xmlWriter->endDocument(); + + return $this->xmlWriter->outputMemory(); + } + + private function addContains($name, Version $version, Type $type) { + $this->xmlWriter->startElement('contains'); + $this->xmlWriter->writeAttribute('name', $name); + $this->xmlWriter->writeAttribute('version', $version->getVersionString()); + + switch (true) { + case $type->isApplication(): { + $this->xmlWriter->writeAttribute('type', 'application'); + break; + } + + case $type->isLibrary(): { + $this->xmlWriter->writeAttribute('type', 'library'); + break; + } + + case $type->isExtension(): { + /* @var $type Extension */ + $this->xmlWriter->writeAttribute('type', 'extension'); + $this->addExtension($type->getApplicationName(), $type->getVersionConstraint()); + break; + } + + default: { + $this->xmlWriter->writeAttribute('type', 'custom'); + } + } + + $this->xmlWriter->endElement(); + } + + private function addCopyright(CopyrightInformation $copyrightInformation) { + $this->xmlWriter->startElement('copyright'); + + foreach($copyrightInformation->getAuthors() as $author) { + $this->xmlWriter->startElement('author'); + $this->xmlWriter->writeAttribute('name', $author->getName()); + $this->xmlWriter->writeAttribute('email', (string) $author->getEmail()); + $this->xmlWriter->endElement(); + } + + $license = $copyrightInformation->getLicense(); + + $this->xmlWriter->startElement('license'); + $this->xmlWriter->writeAttribute('type', $license->getName()); + $this->xmlWriter->writeAttribute('url', $license->getUrl()); + $this->xmlWriter->endElement(); + + $this->xmlWriter->endElement(); + } + + private function addRequirements(RequirementCollection $requirementCollection) { + $phpRequirement = new AnyVersionConstraint(); + $extensions = []; + + foreach($requirementCollection as $requirement) { + if ($requirement instanceof PhpVersionRequirement) { + $phpRequirement = $requirement->getVersionConstraint(); + continue; + } + + if ($requirement instanceof PhpExtensionRequirement) { + $extensions[] = (string) $requirement; + } + } + + $this->xmlWriter->startElement('requires'); + $this->xmlWriter->startElement('php'); + $this->xmlWriter->writeAttribute('version', $phpRequirement->asString()); + + foreach($extensions as $extension) { + $this->xmlWriter->startElement('ext'); + $this->xmlWriter->writeAttribute('name', $extension); + $this->xmlWriter->endElement(); + } + + $this->xmlWriter->endElement(); + $this->xmlWriter->endElement(); + } + + private function addBundles(BundledComponentCollection $bundledComponentCollection) { + if (count($bundledComponentCollection) === 0) { + return; + } + $this->xmlWriter->startElement('bundles'); + + foreach($bundledComponentCollection as $bundledComponent) { + $this->xmlWriter->startElement('component'); + $this->xmlWriter->writeAttribute('name', $bundledComponent->getName()); + $this->xmlWriter->writeAttribute('version', $bundledComponent->getVersion()->getVersionString()); + $this->xmlWriter->endElement(); + } + + $this->xmlWriter->endElement(); + } + + private function addExtension($application, VersionConstraint $versionConstraint) { + $this->xmlWriter->startElement('extension'); + $this->xmlWriter->writeAttribute('for', $application); + $this->xmlWriter->writeAttribute('compatible', $versionConstraint->asString()); + $this->xmlWriter->endElement(); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ManifestLoader { + /** + * @param string $filename + * + * @return Manifest + * + * @throws ManifestLoaderException + */ + public static function fromFile($filename) { + try { + return (new ManifestDocumentMapper())->map( + ManifestDocument::fromFile($filename) + ); + } catch (Exception $e) { + throw new ManifestLoaderException( + sprintf('Loading %s failed.', $filename), + $e->getCode(), + $e + ); + } + } + + /** + * @param string $filename + * + * @return Manifest + * + * @throws ManifestLoaderException + */ + public static function fromPhar($filename) { + return self::fromFile('phar://' . $filename . '/manifest.xml'); + } + + /** + * @param string $manifest + * + * @return Manifest + * + * @throws ManifestLoaderException + */ + public static function fromString($manifest) { + try { + return (new ManifestDocumentMapper())->map( + ManifestDocument::fromString($manifest) + ); + } catch (Exception $e) { + throw new ManifestLoaderException( + 'Processing string failed', + $e->getCode(), + $e + ); + } + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\Version; +use PharIo\Version\Exception as VersionException; +use PharIo\Version\VersionConstraintParser; + +class ManifestDocumentMapper { + /** + * @param ManifestDocument $document + * + * @returns Manifest + * + * @throws ManifestDocumentMapperException + */ + public function map(ManifestDocument $document) { + try { + $contains = $document->getContainsElement(); + $type = $this->mapType($contains); + $copyright = $this->mapCopyright($document->getCopyrightElement()); + $requirements = $this->mapRequirements($document->getRequiresElement()); + $bundledComponents = $this->mapBundledComponents($document); + + return new Manifest( + new ApplicationName($contains->getName()), + new Version($contains->getVersion()), + $type, + $copyright, + $requirements, + $bundledComponents + ); + } catch (VersionException $e) { + throw new ManifestDocumentMapperException($e->getMessage(), $e->getCode(), $e); + } catch (Exception $e) { + throw new ManifestDocumentMapperException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * @param ContainsElement $contains + * + * @return Type + * + * @throws ManifestDocumentMapperException + */ + private function mapType(ContainsElement $contains) { + switch ($contains->getType()) { + case 'application': + return Type::application(); + case 'library': + return Type::library(); + case 'extension': + return $this->mapExtension($contains->getExtensionElement()); + } + + throw new ManifestDocumentMapperException( + sprintf('Unsupported type %s', $contains->getType()) + ); + } + + /** + * @param CopyrightElement $copyright + * + * @return CopyrightInformation + * + * @throws InvalidUrlException + * @throws InvalidEmailException + */ + private function mapCopyright(CopyrightElement $copyright) { + $authors = new AuthorCollection(); + + foreach($copyright->getAuthorElements() as $authorElement) { + $authors->add( + new Author( + $authorElement->getName(), + new Email($authorElement->getEmail()) + ) + ); + } + + $licenseElement = $copyright->getLicenseElement(); + $license = new License( + $licenseElement->getType(), + new Url($licenseElement->getUrl()) + ); + + return new CopyrightInformation( + $authors, + $license + ); + } + + /** + * @param RequiresElement $requires + * + * @return RequirementCollection + * + * @throws ManifestDocumentMapperException + */ + private function mapRequirements(RequiresElement $requires) { + $collection = new RequirementCollection(); + $phpElement = $requires->getPHPElement(); + $parser = new VersionConstraintParser; + + try { + $versionConstraint = $parser->parse($phpElement->getVersion()); + } catch (VersionException $e) { + throw new ManifestDocumentMapperException( + sprintf('Unsupported version constraint - %s', $e->getMessage()), + $e->getCode(), + $e + ); + } + + $collection->add( + new PhpVersionRequirement( + $versionConstraint + ) + ); + + if (!$phpElement->hasExtElements()) { + return $collection; + } + + foreach($phpElement->getExtElements() as $extElement) { + $collection->add( + new PhpExtensionRequirement($extElement->getName()) + ); + } + + return $collection; + } + + /** + * @param ManifestDocument $document + * + * @return BundledComponentCollection + */ + private function mapBundledComponents(ManifestDocument $document) { + $collection = new BundledComponentCollection(); + + if (!$document->hasBundlesElement()) { + return $collection; + } + + foreach($document->getBundlesElement()->getComponentElements() as $componentElement) { + $collection->add( + new BundledComponent( + $componentElement->getName(), + new Version( + $componentElement->getVersion() + ) + ) + ); + } + + return $collection; + } + + /** + * @param ExtensionElement $extension + * + * @return Extension + * + * @throws ManifestDocumentMapperException + */ + private function mapExtension(ExtensionElement $extension) { + try { + $parser = new VersionConstraintParser; + $versionConstraint = $parser->parse($extension->getCompatible()); + + return Type::extension( + new ApplicationName($extension->getFor()), + $versionConstraint + ); + } catch (VersionException $e) { + throw new ManifestDocumentMapperException( + sprintf('Unsupported version constraint - %s', $e->getMessage()), + $e->getCode(), + $e + ); + } + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class Url { + /** + * @var string + */ + private $url; + + /** + * @param string $url + * + * @throws InvalidUrlException + */ + public function __construct($url) { + $this->ensureUrlIsValid($url); + + $this->url = $url; + } + + /** + * @return string + */ + public function __toString() { + return $this->url; + } + + /** + * @param string $url + * + * @throws InvalidUrlException + */ + private function ensureUrlIsValid($url) { + if (filter_var($url, \FILTER_VALIDATE_URL) === false) { + throw new InvalidUrlException; + } + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class PhpExtensionRequirement implements Requirement { + /** + * @var string + */ + private $extension; + + /** + * @param string $extension + */ + public function __construct($extension) { + $this->extension = $extension; + } + + /** + * @return string + */ + public function __toString() { + return $this->extension; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\Version; + +class Manifest { + /** + * @var ApplicationName + */ + private $name; + + /** + * @var Version + */ + private $version; + + /** + * @var Type + */ + private $type; + + /** + * @var CopyrightInformation + */ + private $copyrightInformation; + + /** + * @var RequirementCollection + */ + private $requirements; + + /** + * @var BundledComponentCollection + */ + private $bundledComponents; + + public function __construct(ApplicationName $name, Version $version, Type $type, CopyrightInformation $copyrightInformation, RequirementCollection $requirements, BundledComponentCollection $bundledComponents) { + $this->name = $name; + $this->version = $version; + $this->type = $type; + $this->copyrightInformation = $copyrightInformation; + $this->requirements = $requirements; + $this->bundledComponents = $bundledComponents; + } + + /** + * @return ApplicationName + */ + public function getName() { + return $this->name; + } + + /** + * @return Version + */ + public function getVersion() { + return $this->version; + } + + /** + * @return Type + */ + public function getType() { + return $this->type; + } + + /** + * @return CopyrightInformation + */ + public function getCopyrightInformation() { + return $this->copyrightInformation; + } + + /** + * @return RequirementCollection + */ + public function getRequirements() { + return $this->requirements; + } + + /** + * @return BundledComponentCollection + */ + public function getBundledComponents() { + return $this->bundledComponents; + } + + /** + * @return bool + */ + public function isApplication() { + return $this->type->isApplication(); + } + + /** + * @return bool + */ + public function isLibrary() { + return $this->type->isLibrary(); + } + + /** + * @return bool + */ + public function isExtension() { + return $this->type->isExtension(); + } + + /** + * @param ApplicationName $application + * @param Version|null $version + * + * @return bool + */ + public function isExtensionFor(ApplicationName $application, Version $version = null) { + if (!$this->isExtension()) { + return false; + } + + /** @var Extension $type */ + $type = $this->type; + + if ($version !== null) { + return $type->isCompatibleWith($application, $version); + } + + return $type->isExtensionFor($application); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class BundledComponentCollectionIterator implements \Iterator { + /** + * @var BundledComponent[] + */ + private $bundledComponents = []; + + /** + * @var int + */ + private $position; + + public function __construct(BundledComponentCollection $bundledComponents) { + $this->bundledComponents = $bundledComponents->getBundledComponents(); + } + + public function rewind() { + $this->position = 0; + } + + /** + * @return bool + */ + public function valid() { + return $this->position < count($this->bundledComponents); + } + + /** + * @return int + */ + public function key() { + return $this->position; + } + + /** + * @return BundledComponent + */ + public function current() { + return $this->bundledComponents[$this->position]; + } + + public function next() { + $this->position++; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class Application extends Type { + /** + * @return bool + */ + public function isApplication() { + return true; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\Version; + +class BundledComponent { + /** + * @var string + */ + private $name; + + /** + * @var Version + */ + private $version; + + /** + * @param string $name + * @param Version $version + */ + public function __construct($name, Version $version) { + $this->name = $name; + $this->version = $version; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return Version + */ + public function getVersion() { + return $this->version; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class RequirementCollection implements \Countable, \IteratorAggregate { + /** + * @var Requirement[] + */ + private $requirements = []; + + public function add(Requirement $requirement) { + $this->requirements[] = $requirement; + } + + /** + * @return Requirement[] + */ + public function getRequirements() { + return $this->requirements; + } + + /** + * @return int + */ + public function count() { + return count($this->requirements); + } + + /** + * @return RequirementCollectionIterator + */ + public function getIterator() { + return new RequirementCollectionIterator($this); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class License { + /** + * @var string + */ + private $name; + + /** + * @var Url + */ + private $url; + + public function __construct($name, Url $url) { + $this->name = $name; + $this->url = $url; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return Url + */ + public function getUrl() { + return $this->url; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class Library extends Type { + /** + * @return bool + */ + public function isLibrary() { + return true; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +interface Requirement { +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class Author { + /** + * @var string + */ + private $name; + + /** + * @var Email + */ + private $email; + + /** + * @param string $name + * @param Email $email + */ + public function __construct($name, Email $email) { + $this->name = $name; + $this->email = $email; + } + + /** + * @return string + */ + public function getName() { + return $this->name; + } + + /** + * @return Email + */ + public function getEmail() { + return $this->email; + } + + /** + * @return string + */ + public function __toString() { + return sprintf( + '%s <%s>', + $this->name, + $this->email + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\VersionConstraint; + +class PhpVersionRequirement implements Requirement { + /** + * @var VersionConstraint + */ + private $versionConstraint; + + public function __construct(VersionConstraint $versionConstraint) { + $this->versionConstraint = $versionConstraint; + } + + /** + * @return VersionConstraint + */ + public function getVersionConstraint() { + return $this->versionConstraint; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class BundledComponentCollection implements \Countable, \IteratorAggregate { + /** + * @var BundledComponent[] + */ + private $bundledComponents = []; + + public function add(BundledComponent $bundledComponent) { + $this->bundledComponents[] = $bundledComponent; + } + + /** + * @return BundledComponent[] + */ + public function getBundledComponents() { + return $this->bundledComponents; + } + + /** + * @return int + */ + public function count() { + return count($this->bundledComponents); + } + + /** + * @return BundledComponentCollectionIterator + */ + public function getIterator() { + return new BundledComponentCollectionIterator($this); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class CopyrightInformation { + /** + * @var AuthorCollection + */ + private $authors; + + /** + * @var License + */ + private $license; + + public function __construct(AuthorCollection $authors, License $license) { + $this->authors = $authors; + $this->license = $license; + } + + /** + * @return AuthorCollection + */ + public function getAuthors() { + return $this->authors; + } + + /** + * @return License + */ + public function getLicense() { + return $this->license; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\Version; +use PharIo\Version\VersionConstraint; + +class Extension extends Type { + /** + * @var ApplicationName + */ + private $application; + + /** + * @var VersionConstraint + */ + private $versionConstraint; + + /** + * @param ApplicationName $application + * @param VersionConstraint $versionConstraint + */ + public function __construct(ApplicationName $application, VersionConstraint $versionConstraint) { + $this->application = $application; + $this->versionConstraint = $versionConstraint; + } + + /** + * @return ApplicationName + */ + public function getApplicationName() { + return $this->application; + } + + /** + * @return VersionConstraint + */ + public function getVersionConstraint() { + return $this->versionConstraint; + } + + /** + * @return bool + */ + public function isExtension() { + return true; + } + + /** + * @param ApplicationName $name + * + * @return bool + */ + public function isExtensionFor(ApplicationName $name) { + return $this->application->isEqual($name); + } + + /** + * @param ApplicationName $name + * @param Version $version + * + * @return bool + */ + public function isCompatibleWith(ApplicationName $name, Version $version) { + return $this->isExtensionFor($name) && $this->versionConstraint->complies($version); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +use PharIo\Version\VersionConstraint; + +abstract class Type { + /** + * @return Application + */ + public static function application() { + return new Application; + } + + /** + * @return Library + */ + public static function library() { + return new Library; + } + + /** + * @param ApplicationName $application + * @param VersionConstraint $versionConstraint + * + * @return Extension + */ + public static function extension(ApplicationName $application, VersionConstraint $versionConstraint) { + return new Extension($application, $versionConstraint); + } + + /** + * @return bool + */ + public function isApplication() { + return false; + } + + /** + * @return bool + */ + public function isLibrary() { + return false; + } + + /** + * @return bool + */ + public function isExtension() { + return false; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class AuthorCollectionIterator implements \Iterator { + /** + * @var Author[] + */ + private $authors = []; + + /** + * @var int + */ + private $position; + + public function __construct(AuthorCollection $authors) { + $this->authors = $authors->getAuthors(); + } + + public function rewind() { + $this->position = 0; + } + + /** + * @return bool + */ + public function valid() { + return $this->position < count($this->authors); + } + + /** + * @return int + */ + public function key() { + return $this->position; + } + + /** + * @return Author + */ + public function current() { + return $this->authors[$this->position]; + } + + public function next() { + $this->position++; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class Email { + /** + * @var string + */ + private $email; + + /** + * @param string $email + * + * @throws InvalidEmailException + */ + public function __construct($email) { + $this->ensureEmailIsValid($email); + + $this->email = $email; + } + + /** + * @return string + */ + public function __toString() { + return $this->email; + } + + /** + * @param string $url + * + * @throws InvalidEmailException + */ + private function ensureEmailIsValid($url) { + if (filter_var($url, \FILTER_VALIDATE_EMAIL) === false) { + throw new InvalidEmailException; + } + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class RequirementCollectionIterator implements \Iterator { + /** + * @var Requirement[] + */ + private $requirements = []; + + /** + * @var int + */ + private $position; + + public function __construct(RequirementCollection $requirements) { + $this->requirements = $requirements->getRequirements(); + } + + public function rewind() { + $this->position = 0; + } + + /** + * @return bool + */ + public function valid() { + return $this->position < count($this->requirements); + } + + /** + * @return int + */ + public function key() { + return $this->position; + } + + /** + * @return Requirement + */ + public function current() { + return $this->requirements[$this->position]; + } + + public function next() { + $this->position++; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class AuthorCollection implements \Countable, \IteratorAggregate { + /** + * @var Author[] + */ + private $authors = []; + + public function add(Author $author) { + $this->authors[] = $author; + } + + /** + * @return Author[] + */ + public function getAuthors() { + return $this->authors; + } + + /** + * @return int + */ + public function count() { + return count($this->authors); + } + + /** + * @return AuthorCollectionIterator + */ + public function getIterator() { + return new AuthorCollectionIterator($this); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class ApplicationName { + /** + * @var string + */ + private $name; + + /** + * ApplicationName constructor. + * + * @param string $name + * + * @throws InvalidApplicationNameException + */ + public function __construct($name) { + $this->ensureIsString($name); + $this->ensureValidFormat($name); + $this->name = $name; + } + + /** + * @return string + */ + public function __toString() { + return $this->name; + } + + public function isEqual(ApplicationName $name) { + return $this->name === $name->name; + } + + /** + * @param string $name + * + * @throws InvalidApplicationNameException + */ + private function ensureValidFormat($name) { + if (!preg_match('#\w/\w#', $name)) { + throw new InvalidApplicationNameException( + sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), + InvalidApplicationNameException::InvalidFormat + ); + } + } + + private function ensureIsString($name) { + if (!is_string($name)) { + throw new InvalidApplicationNameException( + 'Name must be a string', + InvalidApplicationNameException::NotAString + ); + } + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class InvalidEmailException extends \InvalidArgumentException implements Exception { +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +interface Exception { +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class InvalidApplicationNameException extends \InvalidArgumentException implements Exception { + const NotAString = 1; + const InvalidFormat = 2; +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Manifest; + +class InvalidUrlException extends \InvalidArgumentException implements Exception { +} +, Sebastian Heuer , Sebastian Bergmann , and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\FileIterator; + +class Iterator extends \FilterIterator +{ + const PREFIX = 0; + const SUFFIX = 1; + + /** + * @var string + */ + private $basePath; + + /** + * @var array + */ + private $suffixes = []; + + /** + * @var array + */ + private $prefixes = []; + + /** + * @var array + */ + private $exclude = []; + + /** + * @param string $basePath + * @param \Iterator $iterator + * @param array $suffixes + * @param array $prefixes + * @param array $exclude + */ + public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = []) + { + $this->basePath = \realpath($basePath); + $this->prefixes = $prefixes; + $this->suffixes = $suffixes; + $this->exclude = \array_filter(\array_map('realpath', $exclude)); + + parent::__construct($iterator); + } + + public function accept() + { + $current = $this->getInnerIterator()->current(); + $filename = $current->getFilename(); + $realPath = $current->getRealPath(); + + return $this->acceptPath($realPath) && + $this->acceptPrefix($filename) && + $this->acceptSuffix($filename); + } + + private function acceptPath(string $path): bool + { + // Filter files in hidden directories by checking path that is relative to the base path. + if (\preg_match('=/\.[^/]*/=', \str_replace($this->basePath, '', $path))) { + return false; + } + + foreach ($this->exclude as $exclude) { + if (\strpos($path, $exclude) === 0) { + return false; + } + } + + return true; + } + + private function acceptPrefix(string $filename): bool + { + return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); + } + + private function acceptSuffix(string $filename): bool + { + return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); + } + + private function acceptSubString(string $filename, array $subStrings, int $type): bool + { + if (empty($subStrings)) { + return true; + } + + $matched = false; + + foreach ($subStrings as $string) { + if (($type === self::PREFIX && \strpos($filename, $string) === 0) || + ($type === self::SUFFIX && + \substr($filename, -1 * \strlen($string)) === $string)) { + $matched = true; + + break; + } + } + + return $matched; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\FileIterator; + +class Facade +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @param bool $commonPath + * + * @return array + */ + public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = false): array + { + if (\is_string($paths)) { + $paths = [$paths]; + } + + $factory = new Factory; + + $iterator = $factory->getFileIterator($paths, $suffixes, $prefixes, $exclude); + + $files = []; + + foreach ($iterator as $file) { + $file = $file->getRealPath(); + + if ($file) { + $files[] = $file; + } + } + + foreach ($paths as $path) { + if (\is_file($path)) { + $files[] = \realpath($path); + } + } + + $files = \array_unique($files); + \sort($files); + + if ($commonPath) { + return [ + 'commonPath' => $this->getCommonPath($files), + 'files' => $files + ]; + } + + return $files; + } + + protected function getCommonPath(array $files): string + { + $count = \count($files); + + if ($count === 0) { + return ''; + } + + if ($count === 1) { + return \dirname($files[0]) . DIRECTORY_SEPARATOR; + } + + $_files = []; + + foreach ($files as $file) { + $_files[] = $_fileParts = \explode(DIRECTORY_SEPARATOR, $file); + + if (empty($_fileParts[0])) { + $_fileParts[0] = DIRECTORY_SEPARATOR; + } + } + + $common = ''; + $done = false; + $j = 0; + $count--; + + while (!$done) { + for ($i = 0; $i < $count; $i++) { + if ($_files[$i][$j] != $_files[$i + 1][$j]) { + $done = true; + + break; + } + } + + if (!$done) { + $common .= $_files[0][$j]; + + if ($j > 0) { + $common .= DIRECTORY_SEPARATOR; + } + } + + $j++; + } + + return DIRECTORY_SEPARATOR . $common; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\FileIterator; + +class Factory +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * + * @return \AppendIterator + */ + public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []): \AppendIterator + { + if (\is_string($paths)) { + $paths = [$paths]; + } + + $paths = $this->getPathsAfterResolvingWildcards($paths); + $exclude = $this->getPathsAfterResolvingWildcards($exclude); + + if (\is_string($prefixes)) { + if ($prefixes !== '') { + $prefixes = [$prefixes]; + } else { + $prefixes = []; + } + } + + if (\is_string($suffixes)) { + if ($suffixes !== '') { + $suffixes = [$suffixes]; + } else { + $suffixes = []; + } + } + + $iterator = new \AppendIterator; + + foreach ($paths as $path) { + if (\is_dir($path)) { + $iterator->append( + new Iterator( + $path, + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS | \RecursiveDirectoryIterator::SKIP_DOTS) + ), + $suffixes, + $prefixes, + $exclude + ) + ); + } + } + + return $iterator; + } + + protected function getPathsAfterResolvingWildcards(array $paths): array + { + $_paths = []; + + foreach ($paths as $path) { + if ($locals = \glob($path, GLOB_ONLYDIR)) { + $_paths = \array_merge($_paths, \array_map('\realpath', $locals)); + } else { + $_paths[] = \realpath($path); + } + } + + return \array_filter($_paths); + } +} +php-file-iterator + +Copyright (c) 2009-2018, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A PHP token. + */ +abstract class PHP_Token +{ + /** + * @var string + */ + protected $text; + + /** + * @var int + */ + protected $line; + + /** + * @var PHP_Token_Stream + */ + protected $tokenStream; + + /** + * @var int + */ + protected $id; + + /** + * @param string $text + * @param int $line + * @param PHP_Token_Stream $tokenStream + * @param int $id + */ + public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id) + { + $this->text = $text; + $this->line = $line; + $this->tokenStream = $tokenStream; + $this->id = $id; + } + + /** + * @return string + */ + public function __toString() + { + return $this->text; + } + + /** + * @return int + */ + public function getLine() + { + return $this->line; + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } +} + +abstract class PHP_TokenWithScope extends PHP_Token +{ + /** + * @var int + */ + protected $endTokenId; + + /** + * Get the docblock for this token + * + * This method will fetch the docblock belonging to the current token. The + * docblock must be placed on the line directly above the token to be + * recognized. + * + * @return string|null Returns the docblock as a string if found + */ + public function getDocblock() + { + $tokens = $this->tokenStream->tokens(); + $currentLineNumber = $tokens[$this->id]->getLine(); + $prevLineNumber = $currentLineNumber - 1; + + for ($i = $this->id - 1; $i; $i--) { + if (!isset($tokens[$i])) { + return; + } + + if ($tokens[$i] instanceof PHP_Token_FUNCTION || + $tokens[$i] instanceof PHP_Token_CLASS || + $tokens[$i] instanceof PHP_Token_TRAIT) { + // Some other trait, class or function, no docblock can be + // used for the current token + break; + } + + $line = $tokens[$i]->getLine(); + + if ($line == $currentLineNumber || + ($line == $prevLineNumber && + $tokens[$i] instanceof PHP_Token_WHITESPACE)) { + continue; + } + + if ($line < $currentLineNumber && + !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) { + break; + } + + return (string) $tokens[$i]; + } + } + + /** + * @return int + */ + public function getEndTokenId() + { + $block = 0; + $i = $this->id; + $tokens = $this->tokenStream->tokens(); + + while ($this->endTokenId === null && isset($tokens[$i])) { + if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || + $tokens[$i] instanceof PHP_Token_DOLLAR_OPEN_CURLY_BRACES || + $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { + $block++; + } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) { + $block--; + + if ($block === 0) { + $this->endTokenId = $i; + } + } elseif (($this instanceof PHP_Token_FUNCTION || + $this instanceof PHP_Token_NAMESPACE) && + $tokens[$i] instanceof PHP_Token_SEMICOLON) { + if ($block === 0) { + $this->endTokenId = $i; + } + } + + $i++; + } + + if ($this->endTokenId === null) { + $this->endTokenId = $this->id; + } + + return $this->endTokenId; + } + + /** + * @return int + */ + public function getEndLine() + { + return $this->tokenStream[$this->getEndTokenId()]->getLine(); + } +} + +abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope +{ + /** + * @return string + */ + public function getVisibility() + { + $tokens = $this->tokenStream->tokens(); + + for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { + if (isset($tokens[$i]) && + ($tokens[$i] instanceof PHP_Token_PRIVATE || + $tokens[$i] instanceof PHP_Token_PROTECTED || + $tokens[$i] instanceof PHP_Token_PUBLIC)) { + return strtolower( + str_replace('PHP_Token_', '', get_class($tokens[$i])) + ); + } + if (isset($tokens[$i]) && + !($tokens[$i] instanceof PHP_Token_STATIC || + $tokens[$i] instanceof PHP_Token_FINAL || + $tokens[$i] instanceof PHP_Token_ABSTRACT)) { + // no keywords; stop visibility search + break; + } + } + } + + /** + * @return string + */ + public function getKeywords() + { + $keywords = []; + $tokens = $this->tokenStream->tokens(); + + for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { + if (isset($tokens[$i]) && + ($tokens[$i] instanceof PHP_Token_PRIVATE || + $tokens[$i] instanceof PHP_Token_PROTECTED || + $tokens[$i] instanceof PHP_Token_PUBLIC)) { + continue; + } + + if (isset($tokens[$i]) && + ($tokens[$i] instanceof PHP_Token_STATIC || + $tokens[$i] instanceof PHP_Token_FINAL || + $tokens[$i] instanceof PHP_Token_ABSTRACT)) { + $keywords[] = strtolower( + str_replace('PHP_Token_', '', get_class($tokens[$i])) + ); + } + } + + return implode(',', $keywords); + } +} + +abstract class PHP_Token_Includes extends PHP_Token +{ + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $type; + + /** + * @return string + */ + public function getName() + { + if ($this->name === null) { + $this->process(); + } + + return $this->name; + } + + /** + * @return string + */ + public function getType() + { + if ($this->type === null) { + $this->process(); + } + + return $this->type; + } + + private function process() + { + $tokens = $this->tokenStream->tokens(); + + if ($tokens[$this->id + 2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) { + $this->name = trim($tokens[$this->id + 2], "'\""); + $this->type = strtolower( + str_replace('PHP_Token_', '', get_class($tokens[$this->id])) + ); + } + } +} + +class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility +{ + /** + * @var array + */ + protected $arguments; + + /** + * @var int + */ + protected $ccn; + + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $signature; + + /** + * @var bool + */ + private $anonymous = false; + + /** + * @return array + */ + public function getArguments() + { + if ($this->arguments !== null) { + return $this->arguments; + } + + $this->arguments = []; + $tokens = $this->tokenStream->tokens(); + $typeDeclaration = null; + + // Search for first token inside brackets + $i = $this->id + 2; + + while (!$tokens[$i - 1] instanceof PHP_Token_OPEN_BRACKET) { + $i++; + } + + while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { + if ($tokens[$i] instanceof PHP_Token_STRING) { + $typeDeclaration = (string) $tokens[$i]; + } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) { + $this->arguments[(string) $tokens[$i]] = $typeDeclaration; + $typeDeclaration = null; + } + + $i++; + } + + return $this->arguments; + } + + /** + * @return string + */ + public function getName() + { + if ($this->name !== null) { + return $this->name; + } + + $tokens = $this->tokenStream->tokens(); + + $i = $this->id + 1; + + if ($tokens[$i] instanceof PHP_Token_WHITESPACE) { + $i++; + } + + if ($tokens[$i] instanceof PHP_Token_AMPERSAND) { + $i++; + } + + if ($tokens[$i + 1] instanceof PHP_Token_OPEN_BRACKET) { + $this->name = (string) $tokens[$i]; + } elseif ($tokens[$i + 1] instanceof PHP_Token_WHITESPACE && $tokens[$i + 2] instanceof PHP_Token_OPEN_BRACKET) { + $this->name = (string) $tokens[$i]; + } else { + $this->anonymous = true; + + $this->name = sprintf( + 'anonymousFunction:%s#%s', + $this->getLine(), + $this->getId() + ); + } + + if (!$this->isAnonymous()) { + for ($i = $this->id; $i; --$i) { + if ($tokens[$i] instanceof PHP_Token_NAMESPACE) { + $this->name = $tokens[$i]->getName() . '\\' . $this->name; + + break; + } + + if ($tokens[$i] instanceof PHP_Token_INTERFACE) { + break; + } + } + } + + return $this->name; + } + + /** + * @return int + */ + public function getCCN() + { + if ($this->ccn !== null) { + return $this->ccn; + } + + $this->ccn = 1; + $end = $this->getEndTokenId(); + $tokens = $this->tokenStream->tokens(); + + for ($i = $this->id; $i <= $end; $i++) { + switch (get_class($tokens[$i])) { + case 'PHP_Token_IF': + case 'PHP_Token_ELSEIF': + case 'PHP_Token_FOR': + case 'PHP_Token_FOREACH': + case 'PHP_Token_WHILE': + case 'PHP_Token_CASE': + case 'PHP_Token_CATCH': + case 'PHP_Token_BOOLEAN_AND': + case 'PHP_Token_LOGICAL_AND': + case 'PHP_Token_BOOLEAN_OR': + case 'PHP_Token_LOGICAL_OR': + case 'PHP_Token_QUESTION_MARK': + $this->ccn++; + break; + } + } + + return $this->ccn; + } + + /** + * @return string + */ + public function getSignature() + { + if ($this->signature !== null) { + return $this->signature; + } + + if ($this->isAnonymous()) { + $this->signature = 'anonymousFunction'; + $i = $this->id + 1; + } else { + $this->signature = ''; + $i = $this->id + 2; + } + + $tokens = $this->tokenStream->tokens(); + + while (isset($tokens[$i]) && + !$tokens[$i] instanceof PHP_Token_OPEN_CURLY && + !$tokens[$i] instanceof PHP_Token_SEMICOLON) { + $this->signature .= $tokens[$i++]; + } + + $this->signature = trim($this->signature); + + return $this->signature; + } + + /** + * @return bool + */ + public function isAnonymous() + { + return $this->anonymous; + } +} + +class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility +{ + /** + * @var array + */ + protected $interfaces; + + /** + * @return string + */ + public function getName() + { + return (string) $this->tokenStream[$this->id + 2]; + } + + /** + * @return bool + */ + public function hasParent() + { + return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS; + } + + /** + * @return array + */ + public function getPackage() + { + $className = $this->getName(); + $docComment = $this->getDocblock(); + + $result = [ + 'namespace' => '', + 'fullPackage' => '', + 'category' => '', + 'package' => '', + 'subpackage' => '' + ]; + + for ($i = $this->id; $i; --$i) { + if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) { + $result['namespace'] = $this->tokenStream[$i]->getName(); + break; + } + } + + if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['category'] = $matches[1]; + } + + if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['package'] = $matches[1]; + $result['fullPackage'] = $matches[1]; + } + + if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) { + $result['subpackage'] = $matches[1]; + $result['fullPackage'] .= '.' . $matches[1]; + } + + if (empty($result['fullPackage'])) { + $result['fullPackage'] = $this->arrayToName( + explode('_', str_replace('\\', '_', $className)), + '.' + ); + } + + return $result; + } + + /** + * @param array $parts + * @param string $join + * + * @return string + */ + protected function arrayToName(array $parts, $join = '\\') + { + $result = ''; + + if (count($parts) > 1) { + array_pop($parts); + + $result = implode($join, $parts); + } + + return $result; + } + + /** + * @return bool|string + */ + public function getParent() + { + if (!$this->hasParent()) { + return false; + } + + $i = $this->id + 6; + $tokens = $this->tokenStream->tokens(); + $className = (string) $tokens[$i]; + + while (isset($tokens[$i + 1]) && + !$tokens[$i + 1] instanceof PHP_Token_WHITESPACE) { + $className .= (string) $tokens[++$i]; + } + + return $className; + } + + /** + * @return bool + */ + public function hasInterfaces() + { + return (isset($this->tokenStream[$this->id + 4]) && + $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) || + (isset($this->tokenStream[$this->id + 8]) && + $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS); + } + + /** + * @return array|bool + */ + public function getInterfaces() + { + if ($this->interfaces !== null) { + return $this->interfaces; + } + + if (!$this->hasInterfaces()) { + return ($this->interfaces = false); + } + + if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) { + $i = $this->id + 3; + } else { + $i = $this->id + 7; + } + + $tokens = $this->tokenStream->tokens(); + + while (!$tokens[$i + 1] instanceof PHP_Token_OPEN_CURLY) { + $i++; + + if ($tokens[$i] instanceof PHP_Token_STRING) { + $this->interfaces[] = (string) $tokens[$i]; + } + } + + return $this->interfaces; + } +} + +class PHP_Token_ABSTRACT extends PHP_Token +{ +} + +class PHP_Token_AMPERSAND extends PHP_Token +{ +} + +class PHP_Token_AND_EQUAL extends PHP_Token +{ +} + +class PHP_Token_ARRAY extends PHP_Token +{ +} + +class PHP_Token_ARRAY_CAST extends PHP_Token +{ +} + +class PHP_Token_AS extends PHP_Token +{ +} + +class PHP_Token_AT extends PHP_Token +{ +} + +class PHP_Token_BACKTICK extends PHP_Token +{ +} + +class PHP_Token_BAD_CHARACTER extends PHP_Token +{ +} + +class PHP_Token_BOOLEAN_AND extends PHP_Token +{ +} + +class PHP_Token_BOOLEAN_OR extends PHP_Token +{ +} + +class PHP_Token_BOOL_CAST extends PHP_Token +{ +} + +class PHP_Token_BREAK extends PHP_Token +{ +} + +class PHP_Token_CARET extends PHP_Token +{ +} + +class PHP_Token_CASE extends PHP_Token +{ +} + +class PHP_Token_CATCH extends PHP_Token +{ +} + +class PHP_Token_CHARACTER extends PHP_Token +{ +} + +class PHP_Token_CLASS extends PHP_Token_INTERFACE +{ + /** + * @var bool + */ + private $anonymous = false; + + /** + * @var string + */ + private $name; + + /** + * @return string + */ + public function getName() + { + if ($this->name !== null) { + return $this->name; + } + + $next = $this->tokenStream[$this->id + 1]; + + if ($next instanceof PHP_Token_WHITESPACE) { + $next = $this->tokenStream[$this->id + 2]; + } + + if ($next instanceof PHP_Token_STRING) { + $this->name =(string) $next; + + return $this->name; + } + + if ($next instanceof PHP_Token_OPEN_CURLY || + $next instanceof PHP_Token_EXTENDS || + $next instanceof PHP_Token_IMPLEMENTS) { + + $this->name = sprintf( + 'AnonymousClass:%s#%s', + $this->getLine(), + $this->getId() + ); + + $this->anonymous = true; + + return $this->name; + } + } + + public function isAnonymous() + { + return $this->anonymous; + } +} + +class PHP_Token_CLASS_C extends PHP_Token +{ +} + +class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token +{ +} + +class PHP_Token_CLONE extends PHP_Token +{ +} + +class PHP_Token_CLOSE_BRACKET extends PHP_Token +{ +} + +class PHP_Token_CLOSE_CURLY extends PHP_Token +{ +} + +class PHP_Token_CLOSE_SQUARE extends PHP_Token +{ +} + +class PHP_Token_CLOSE_TAG extends PHP_Token +{ +} + +class PHP_Token_COLON extends PHP_Token +{ +} + +class PHP_Token_COMMA extends PHP_Token +{ +} + +class PHP_Token_COMMENT extends PHP_Token +{ +} + +class PHP_Token_CONCAT_EQUAL extends PHP_Token +{ +} + +class PHP_Token_CONST extends PHP_Token +{ +} + +class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token +{ +} + +class PHP_Token_CONTINUE extends PHP_Token +{ +} + +class PHP_Token_CURLY_OPEN extends PHP_Token +{ +} + +class PHP_Token_DEC extends PHP_Token +{ +} + +class PHP_Token_DECLARE extends PHP_Token +{ +} + +class PHP_Token_DEFAULT extends PHP_Token +{ +} + +class PHP_Token_DIV extends PHP_Token +{ +} + +class PHP_Token_DIV_EQUAL extends PHP_Token +{ +} + +class PHP_Token_DNUMBER extends PHP_Token +{ +} + +class PHP_Token_DO extends PHP_Token +{ +} + +class PHP_Token_DOC_COMMENT extends PHP_Token +{ +} + +class PHP_Token_DOLLAR extends PHP_Token +{ +} + +class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token +{ +} + +class PHP_Token_DOT extends PHP_Token +{ +} + +class PHP_Token_DOUBLE_ARROW extends PHP_Token +{ +} + +class PHP_Token_DOUBLE_CAST extends PHP_Token +{ +} + +class PHP_Token_DOUBLE_COLON extends PHP_Token +{ +} + +class PHP_Token_DOUBLE_QUOTES extends PHP_Token +{ +} + +class PHP_Token_ECHO extends PHP_Token +{ +} + +class PHP_Token_ELSE extends PHP_Token +{ +} + +class PHP_Token_ELSEIF extends PHP_Token +{ +} + +class PHP_Token_EMPTY extends PHP_Token +{ +} + +class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token +{ +} + +class PHP_Token_ENDDECLARE extends PHP_Token +{ +} + +class PHP_Token_ENDFOR extends PHP_Token +{ +} + +class PHP_Token_ENDFOREACH extends PHP_Token +{ +} + +class PHP_Token_ENDIF extends PHP_Token +{ +} + +class PHP_Token_ENDSWITCH extends PHP_Token +{ +} + +class PHP_Token_ENDWHILE extends PHP_Token +{ +} + +class PHP_Token_END_HEREDOC extends PHP_Token +{ +} + +class PHP_Token_EQUAL extends PHP_Token +{ +} + +class PHP_Token_EVAL extends PHP_Token +{ +} + +class PHP_Token_EXCLAMATION_MARK extends PHP_Token +{ +} + +class PHP_Token_EXIT extends PHP_Token +{ +} + +class PHP_Token_EXTENDS extends PHP_Token +{ +} + +class PHP_Token_FILE extends PHP_Token +{ +} + +class PHP_Token_FINAL extends PHP_Token +{ +} + +class PHP_Token_FOR extends PHP_Token +{ +} + +class PHP_Token_FOREACH extends PHP_Token +{ +} + +class PHP_Token_FUNC_C extends PHP_Token +{ +} + +class PHP_Token_GLOBAL extends PHP_Token +{ +} + +class PHP_Token_GT extends PHP_Token +{ +} + +class PHP_Token_IF extends PHP_Token +{ +} + +class PHP_Token_IMPLEMENTS extends PHP_Token +{ +} + +class PHP_Token_INC extends PHP_Token +{ +} + +class PHP_Token_INCLUDE extends PHP_Token_Includes +{ +} + +class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes +{ +} + +class PHP_Token_INLINE_HTML extends PHP_Token +{ +} + +class PHP_Token_INSTANCEOF extends PHP_Token +{ +} + +class PHP_Token_INT_CAST extends PHP_Token +{ +} + +class PHP_Token_ISSET extends PHP_Token +{ +} + +class PHP_Token_IS_EQUAL extends PHP_Token +{ +} + +class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token +{ +} + +class PHP_Token_IS_IDENTICAL extends PHP_Token +{ +} + +class PHP_Token_IS_NOT_EQUAL extends PHP_Token +{ +} + +class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token +{ +} + +class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token +{ +} + +class PHP_Token_LINE extends PHP_Token +{ +} + +class PHP_Token_LIST extends PHP_Token +{ +} + +class PHP_Token_LNUMBER extends PHP_Token +{ +} + +class PHP_Token_LOGICAL_AND extends PHP_Token +{ +} + +class PHP_Token_LOGICAL_OR extends PHP_Token +{ +} + +class PHP_Token_LOGICAL_XOR extends PHP_Token +{ +} + +class PHP_Token_LT extends PHP_Token +{ +} + +class PHP_Token_METHOD_C extends PHP_Token +{ +} + +class PHP_Token_MINUS extends PHP_Token +{ +} + +class PHP_Token_MINUS_EQUAL extends PHP_Token +{ +} + +class PHP_Token_MOD_EQUAL extends PHP_Token +{ +} + +class PHP_Token_MULT extends PHP_Token +{ +} + +class PHP_Token_MUL_EQUAL extends PHP_Token +{ +} + +class PHP_Token_NEW extends PHP_Token +{ +} + +class PHP_Token_NUM_STRING extends PHP_Token +{ +} + +class PHP_Token_OBJECT_CAST extends PHP_Token +{ +} + +class PHP_Token_OBJECT_OPERATOR extends PHP_Token +{ +} + +class PHP_Token_OPEN_BRACKET extends PHP_Token +{ +} + +class PHP_Token_OPEN_CURLY extends PHP_Token +{ +} + +class PHP_Token_OPEN_SQUARE extends PHP_Token +{ +} + +class PHP_Token_OPEN_TAG extends PHP_Token +{ +} + +class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token +{ +} + +class PHP_Token_OR_EQUAL extends PHP_Token +{ +} + +class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token +{ +} + +class PHP_Token_PERCENT extends PHP_Token +{ +} + +class PHP_Token_PIPE extends PHP_Token +{ +} + +class PHP_Token_PLUS extends PHP_Token +{ +} + +class PHP_Token_PLUS_EQUAL extends PHP_Token +{ +} + +class PHP_Token_PRINT extends PHP_Token +{ +} + +class PHP_Token_PRIVATE extends PHP_Token +{ +} + +class PHP_Token_PROTECTED extends PHP_Token +{ +} + +class PHP_Token_PUBLIC extends PHP_Token +{ +} + +class PHP_Token_QUESTION_MARK extends PHP_Token +{ +} + +class PHP_Token_REQUIRE extends PHP_Token_Includes +{ +} + +class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes +{ +} + +class PHP_Token_RETURN extends PHP_Token +{ +} + +class PHP_Token_SEMICOLON extends PHP_Token +{ +} + +class PHP_Token_SL extends PHP_Token +{ +} + +class PHP_Token_SL_EQUAL extends PHP_Token +{ +} + +class PHP_Token_SR extends PHP_Token +{ +} + +class PHP_Token_SR_EQUAL extends PHP_Token +{ +} + +class PHP_Token_START_HEREDOC extends PHP_Token +{ +} + +class PHP_Token_STATIC extends PHP_Token +{ +} + +class PHP_Token_STRING extends PHP_Token +{ +} + +class PHP_Token_STRING_CAST extends PHP_Token +{ +} + +class PHP_Token_STRING_VARNAME extends PHP_Token +{ +} + +class PHP_Token_SWITCH extends PHP_Token +{ +} + +class PHP_Token_THROW extends PHP_Token +{ +} + +class PHP_Token_TILDE extends PHP_Token +{ +} + +class PHP_Token_TRY extends PHP_Token +{ +} + +class PHP_Token_UNSET extends PHP_Token +{ +} + +class PHP_Token_UNSET_CAST extends PHP_Token +{ +} + +class PHP_Token_USE extends PHP_Token +{ +} + +class PHP_Token_USE_FUNCTION extends PHP_Token +{ +} + +class PHP_Token_VAR extends PHP_Token +{ +} + +class PHP_Token_VARIABLE extends PHP_Token +{ +} + +class PHP_Token_WHILE extends PHP_Token +{ +} + +class PHP_Token_WHITESPACE extends PHP_Token +{ +} + +class PHP_Token_XOR_EQUAL extends PHP_Token +{ +} + +// Tokens introduced in PHP 5.1 +class PHP_Token_HALT_COMPILER extends PHP_Token +{ +} + +// Tokens introduced in PHP 5.3 +class PHP_Token_DIR extends PHP_Token +{ +} + +class PHP_Token_GOTO extends PHP_Token +{ +} + +class PHP_Token_NAMESPACE extends PHP_TokenWithScope +{ + /** + * @return string + */ + public function getName() + { + $tokens = $this->tokenStream->tokens(); + $namespace = (string) $tokens[$this->id + 2]; + + for ($i = $this->id + 3;; $i += 2) { + if (isset($tokens[$i]) && + $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) { + $namespace .= '\\' . $tokens[$i + 1]; + } else { + break; + } + } + + return $namespace; + } +} + +class PHP_Token_NS_C extends PHP_Token +{ +} + +class PHP_Token_NS_SEPARATOR extends PHP_Token +{ +} + +// Tokens introduced in PHP 5.4 +class PHP_Token_CALLABLE extends PHP_Token +{ +} + +class PHP_Token_INSTEADOF extends PHP_Token +{ +} + +class PHP_Token_TRAIT extends PHP_Token_INTERFACE +{ +} + +class PHP_Token_TRAIT_C extends PHP_Token +{ +} + +// Tokens introduced in PHP 5.5 +class PHP_Token_FINALLY extends PHP_Token +{ +} + +class PHP_Token_YIELD extends PHP_Token +{ +} + +// Tokens introduced in PHP 5.6 +class PHP_Token_ELLIPSIS extends PHP_Token +{ +} + +class PHP_Token_POW extends PHP_Token +{ +} + +class PHP_Token_POW_EQUAL extends PHP_Token +{ +} + +// Tokens introduced in PHP 7.0 +class PHP_Token_COALESCE extends PHP_Token +{ +} + +class PHP_Token_SPACESHIP extends PHP_Token +{ +} + +class PHP_Token_YIELD_FROM extends PHP_Token +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A caching factory for token stream objects. + */ +class PHP_Token_Stream_CachingFactory +{ + /** + * @var array + */ + protected static $cache = []; + + /** + * @param string $filename + * + * @return PHP_Token_Stream + */ + public static function get($filename) + { + if (!isset(self::$cache[$filename])) { + self::$cache[$filename] = new PHP_Token_Stream($filename); + } + + return self::$cache[$filename]; + } + + /** + * @param string $filename + */ + public static function clear($filename = null) + { + if (is_string($filename)) { + unset(self::$cache[$filename]); + } else { + self::$cache = []; + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A stream of PHP tokens. + */ +class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator +{ + /** + * @var array + */ + protected static $customTokens = [ + '(' => 'PHP_Token_OPEN_BRACKET', + ')' => 'PHP_Token_CLOSE_BRACKET', + '[' => 'PHP_Token_OPEN_SQUARE', + ']' => 'PHP_Token_CLOSE_SQUARE', + '{' => 'PHP_Token_OPEN_CURLY', + '}' => 'PHP_Token_CLOSE_CURLY', + ';' => 'PHP_Token_SEMICOLON', + '.' => 'PHP_Token_DOT', + ',' => 'PHP_Token_COMMA', + '=' => 'PHP_Token_EQUAL', + '<' => 'PHP_Token_LT', + '>' => 'PHP_Token_GT', + '+' => 'PHP_Token_PLUS', + '-' => 'PHP_Token_MINUS', + '*' => 'PHP_Token_MULT', + '/' => 'PHP_Token_DIV', + '?' => 'PHP_Token_QUESTION_MARK', + '!' => 'PHP_Token_EXCLAMATION_MARK', + ':' => 'PHP_Token_COLON', + '"' => 'PHP_Token_DOUBLE_QUOTES', + '@' => 'PHP_Token_AT', + '&' => 'PHP_Token_AMPERSAND', + '%' => 'PHP_Token_PERCENT', + '|' => 'PHP_Token_PIPE', + '$' => 'PHP_Token_DOLLAR', + '^' => 'PHP_Token_CARET', + '~' => 'PHP_Token_TILDE', + '`' => 'PHP_Token_BACKTICK' + ]; + + /** + * @var string + */ + protected $filename; + + /** + * @var array + */ + protected $tokens = []; + + /** + * @var int + */ + protected $position = 0; + + /** + * @var array + */ + protected $linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; + + /** + * @var array + */ + protected $classes; + + /** + * @var array + */ + protected $functions; + + /** + * @var array + */ + protected $includes; + + /** + * @var array + */ + protected $interfaces; + + /** + * @var array + */ + protected $traits; + + /** + * @var array + */ + protected $lineToFunctionMap = []; + + /** + * Constructor. + * + * @param string $sourceCode + */ + public function __construct($sourceCode) + { + if (is_file($sourceCode)) { + $this->filename = $sourceCode; + $sourceCode = file_get_contents($sourceCode); + } + + $this->scan($sourceCode); + } + + /** + * Destructor. + */ + public function __destruct() + { + $this->tokens = []; + } + + /** + * @return string + */ + public function __toString() + { + $buffer = ''; + + foreach ($this as $token) { + $buffer .= $token; + } + + return $buffer; + } + + /** + * @return string + */ + public function getFilename() + { + return $this->filename; + } + + /** + * Scans the source for sequences of characters and converts them into a + * stream of tokens. + * + * @param string $sourceCode + */ + protected function scan($sourceCode) + { + $id = 0; + $line = 1; + $tokens = token_get_all($sourceCode); + $numTokens = count($tokens); + + $lastNonWhitespaceTokenWasDoubleColon = false; + + for ($i = 0; $i < $numTokens; ++$i) { + $token = $tokens[$i]; + $skip = 0; + + if (is_array($token)) { + $name = substr(token_name($token[0]), 2); + $text = $token[1]; + + if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') { + $name = 'CLASS_NAME_CONSTANT'; + } elseif ($name == 'USE' && isset($tokens[$i + 2][0]) && $tokens[$i + 2][0] == T_FUNCTION) { + $name = 'USE_FUNCTION'; + $text .= $tokens[$i + 1][1] . $tokens[$i + 2][1]; + $skip = 2; + } + + $tokenClass = 'PHP_Token_' . $name; + } else { + $text = $token; + $tokenClass = self::$customTokens[$token]; + } + + $this->tokens[] = new $tokenClass($text, $line, $this, $id++); + $lines = substr_count($text, "\n"); + $line += $lines; + + if ($tokenClass == 'PHP_Token_HALT_COMPILER') { + break; + } elseif ($tokenClass == 'PHP_Token_COMMENT' || + $tokenClass == 'PHP_Token_DOC_COMMENT') { + $this->linesOfCode['cloc'] += $lines + 1; + } + + if ($name == 'DOUBLE_COLON') { + $lastNonWhitespaceTokenWasDoubleColon = true; + } elseif ($name != 'WHITESPACE') { + $lastNonWhitespaceTokenWasDoubleColon = false; + } + + $i += $skip; + } + + $this->linesOfCode['loc'] = substr_count($sourceCode, "\n"); + $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - + $this->linesOfCode['cloc']; + } + + /** + * @return int + */ + public function count() + { + return count($this->tokens); + } + + /** + * @return PHP_Token[] + */ + public function tokens() + { + return $this->tokens; + } + + /** + * @return array + */ + public function getClasses() + { + if ($this->classes !== null) { + return $this->classes; + } + + $this->parse(); + + return $this->classes; + } + + /** + * @return array + */ + public function getFunctions() + { + if ($this->functions !== null) { + return $this->functions; + } + + $this->parse(); + + return $this->functions; + } + + /** + * @return array + */ + public function getInterfaces() + { + if ($this->interfaces !== null) { + return $this->interfaces; + } + + $this->parse(); + + return $this->interfaces; + } + + /** + * @return array + */ + public function getTraits() + { + if ($this->traits !== null) { + return $this->traits; + } + + $this->parse(); + + return $this->traits; + } + + /** + * Gets the names of all files that have been included + * using include(), include_once(), require() or require_once(). + * + * Parameter $categorize set to TRUE causing this function to return a + * multi-dimensional array with categories in the keys of the first dimension + * and constants and their values in the second dimension. + * + * Parameter $category allow to filter following specific inclusion type + * + * @param bool $categorize OPTIONAL + * @param string $category OPTIONAL Either 'require_once', 'require', + * 'include_once', 'include'. + * + * @return array + */ + public function getIncludes($categorize = false, $category = null) + { + if ($this->includes === null) { + $this->includes = [ + 'require_once' => [], + 'require' => [], + 'include_once' => [], + 'include' => [] + ]; + + foreach ($this->tokens as $token) { + switch (get_class($token)) { + case 'PHP_Token_REQUIRE_ONCE': + case 'PHP_Token_REQUIRE': + case 'PHP_Token_INCLUDE_ONCE': + case 'PHP_Token_INCLUDE': + $this->includes[$token->getType()][] = $token->getName(); + break; + } + } + } + + if (isset($this->includes[$category])) { + $includes = $this->includes[$category]; + } elseif ($categorize === false) { + $includes = array_merge( + $this->includes['require_once'], + $this->includes['require'], + $this->includes['include_once'], + $this->includes['include'] + ); + } else { + $includes = $this->includes; + } + + return $includes; + } + + /** + * Returns the name of the function or method a line belongs to. + * + * @return string or null if the line is not in a function or method + */ + public function getFunctionForLine($line) + { + $this->parse(); + + if (isset($this->lineToFunctionMap[$line])) { + return $this->lineToFunctionMap[$line]; + } + } + + protected function parse() + { + $this->interfaces = []; + $this->classes = []; + $this->traits = []; + $this->functions = []; + $class = []; + $classEndLine = []; + $trait = false; + $traitEndLine = false; + $interface = false; + $interfaceEndLine = false; + + foreach ($this->tokens as $token) { + switch (get_class($token)) { + case 'PHP_Token_HALT_COMPILER': + return; + + case 'PHP_Token_INTERFACE': + $interface = $token->getName(); + $interfaceEndLine = $token->getEndLine(); + + $this->interfaces[$interface] = [ + 'methods' => [], + 'parent' => $token->getParent(), + 'keywords' => $token->getKeywords(), + 'docblock' => $token->getDocblock(), + 'startLine' => $token->getLine(), + 'endLine' => $interfaceEndLine, + 'package' => $token->getPackage(), + 'file' => $this->filename + ]; + break; + + case 'PHP_Token_CLASS': + case 'PHP_Token_TRAIT': + $tmp = [ + 'methods' => [], + 'parent' => $token->getParent(), + 'interfaces'=> $token->getInterfaces(), + 'keywords' => $token->getKeywords(), + 'docblock' => $token->getDocblock(), + 'startLine' => $token->getLine(), + 'endLine' => $token->getEndLine(), + 'package' => $token->getPackage(), + 'file' => $this->filename + ]; + + if ($token instanceof PHP_Token_CLASS) { + $class[] = $token->getName(); + $classEndLine[] = $token->getEndLine(); + + $this->classes[$class[count($class) - 1]] = $tmp; + } else { + $trait = $token->getName(); + $traitEndLine = $token->getEndLine(); + $this->traits[$trait] = $tmp; + } + break; + + case 'PHP_Token_FUNCTION': + $name = $token->getName(); + $tmp = [ + 'docblock' => $token->getDocblock(), + 'keywords' => $token->getKeywords(), + 'visibility'=> $token->getVisibility(), + 'signature' => $token->getSignature(), + 'startLine' => $token->getLine(), + 'endLine' => $token->getEndLine(), + 'ccn' => $token->getCCN(), + 'file' => $this->filename + ]; + + if (empty($class) && + $trait === false && + $interface === false) { + $this->functions[$name] = $tmp; + + $this->addFunctionToMap( + $name, + $tmp['startLine'], + $tmp['endLine'] + ); + } elseif (!empty($class)) { + $this->classes[$class[count($class) - 1]]['methods'][$name] = $tmp; + + $this->addFunctionToMap( + $class[count($class) - 1] . '::' . $name, + $tmp['startLine'], + $tmp['endLine'] + ); + } elseif ($trait !== false) { + $this->traits[$trait]['methods'][$name] = $tmp; + + $this->addFunctionToMap( + $trait . '::' . $name, + $tmp['startLine'], + $tmp['endLine'] + ); + } else { + $this->interfaces[$interface]['methods'][$name] = $tmp; + } + break; + + case 'PHP_Token_CLOSE_CURLY': + if (!empty($classEndLine) && + $classEndLine[count($classEndLine) - 1] == $token->getLine()) { + array_pop($classEndLine); + array_pop($class); + } elseif ($traitEndLine !== false && + $traitEndLine == $token->getLine()) { + $trait = false; + $traitEndLine = false; + } elseif ($interfaceEndLine !== false && + $interfaceEndLine == $token->getLine()) { + $interface = false; + $interfaceEndLine = false; + } + break; + } + } + } + + /** + * @return array + */ + public function getLinesOfCode() + { + return $this->linesOfCode; + } + + /** + */ + public function rewind() + { + $this->position = 0; + } + + /** + * @return bool + */ + public function valid() + { + return isset($this->tokens[$this->position]); + } + + /** + * @return int + */ + public function key() + { + return $this->position; + } + + /** + * @return PHP_Token + */ + public function current() + { + return $this->tokens[$this->position]; + } + + /** + */ + public function next() + { + $this->position++; + } + + /** + * @param int $offset + * + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->tokens[$offset]); + } + + /** + * @param int $offset + * + * @return mixed + * + * @throws OutOfBoundsException + */ + public function offsetGet($offset) + { + if (!$this->offsetExists($offset)) { + throw new OutOfBoundsException( + sprintf( + 'No token at position "%s"', + $offset + ) + ); + } + + return $this->tokens[$offset]; + } + + /** + * @param int $offset + * @param mixed $value + */ + public function offsetSet($offset, $value) + { + $this->tokens[$offset] = $value; + } + + /** + * @param int $offset + * + * @throws OutOfBoundsException + */ + public function offsetUnset($offset) + { + if (!$this->offsetExists($offset)) { + throw new OutOfBoundsException( + sprintf( + 'No token at position "%s"', + $offset + ) + ); + } + + unset($this->tokens[$offset]); + } + + /** + * Seek to an absolute position. + * + * @param int $position + * + * @throws OutOfBoundsException + */ + public function seek($position) + { + $this->position = $position; + + if (!$this->valid()) { + throw new OutOfBoundsException( + sprintf( + 'No token at position "%s"', + $this->position + ) + ); + } + } + + /** + * @param string $name + * @param int $startLine + * @param int $endLine + */ + private function addFunctionToMap($name, $startLine, $endLine) + { + for ($line = $startLine; $line <= $endLine; $line++) { + $this->lineToFunctionMap[$line] = $name; + } + } +} +php-token-stream + +Copyright (c) 2009-2018, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + + + + This Schema file defines the rules by which the XML configuration file of PHPUnit 7.5 may be structured. + + + + + + Root Element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The main type specifying the document structure + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Webmozart\Assert; + +use ArrayAccess; +use BadMethodCallException; +use Closure; +use Countable; +use Exception; +use InvalidArgumentException; +use Throwable; +use Traversable; + +/** + * Efficient assertions to validate the input/output of your methods. + * + * @method static void nullOrString($value, $message = '') + * @method static void nullOrStringNotEmpty($value, $message = '') + * @method static void nullOrInteger($value, $message = '') + * @method static void nullOrIntegerish($value, $message = '') + * @method static void nullOrFloat($value, $message = '') + * @method static void nullOrNumeric($value, $message = '') + * @method static void nullOrNatural($value, $message = '') + * @method static void nullOrBoolean($value, $message = '') + * @method static void nullOrScalar($value, $message = '') + * @method static void nullOrObject($value, $message = '') + * @method static void nullOrResource($value, $type = null, $message = '') + * @method static void nullOrIsCallable($value, $message = '') + * @method static void nullOrIsArray($value, $message = '') + * @method static void nullOrIsTraversable($value, $message = '') + * @method static void nullOrIsArrayAccessible($value, $message = '') + * @method static void nullOrIsCountable($value, $message = '') + * @method static void nullOrIsIterable($value, $message = '') + * @method static void nullOrIsInstanceOf($value, $class, $message = '') + * @method static void nullOrNotInstanceOf($value, $class, $message = '') + * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '') + * @method static void nullOrIsEmpty($value, $message = '') + * @method static void nullOrNotEmpty($value, $message = '') + * @method static void nullOrTrue($value, $message = '') + * @method static void nullOrFalse($value, $message = '') + * @method static void nullOrIp($value, $message = '') + * @method static void nullOrIpv4($value, $message = '') + * @method static void nullOrIpv6($value, $message = '') + * @method static void nullOrEq($value, $value2, $message = '') + * @method static void nullOrNotEq($value,$value2, $message = '') + * @method static void nullOrSame($value, $value2, $message = '') + * @method static void nullOrNotSame($value, $value2, $message = '') + * @method static void nullOrGreaterThan($value, $value2, $message = '') + * @method static void nullOrGreaterThanEq($value, $value2, $message = '') + * @method static void nullOrLessThan($value, $value2, $message = '') + * @method static void nullOrLessThanEq($value, $value2, $message = '') + * @method static void nullOrRange($value, $min, $max, $message = '') + * @method static void nullOrOneOf($value, $values, $message = '') + * @method static void nullOrContains($value, $subString, $message = '') + * @method static void nullOrNotContains($value, $subString, $message = '') + * @method static void nullOrNotWhitespaceOnly($value, $message = '') + * @method static void nullOrStartsWith($value, $prefix, $message = '') + * @method static void nullOrStartsWithLetter($value, $message = '') + * @method static void nullOrEndsWith($value, $suffix, $message = '') + * @method static void nullOrRegex($value, $pattern, $message = '') + * @method static void nullOrNotRegex($value, $pattern, $message = '') + * @method static void nullOrAlpha($value, $message = '') + * @method static void nullOrDigits($value, $message = '') + * @method static void nullOrAlnum($value, $message = '') + * @method static void nullOrLower($value, $message = '') + * @method static void nullOrUpper($value, $message = '') + * @method static void nullOrLength($value, $length, $message = '') + * @method static void nullOrMinLength($value, $min, $message = '') + * @method static void nullOrMaxLength($value, $max, $message = '') + * @method static void nullOrLengthBetween($value, $min, $max, $message = '') + * @method static void nullOrFileExists($value, $message = '') + * @method static void nullOrFile($value, $message = '') + * @method static void nullOrDirectory($value, $message = '') + * @method static void nullOrReadable($value, $message = '') + * @method static void nullOrWritable($value, $message = '') + * @method static void nullOrClassExists($value, $message = '') + * @method static void nullOrSubclassOf($value, $class, $message = '') + * @method static void nullOrInterfaceExists($value, $message = '') + * @method static void nullOrImplementsInterface($value, $interface, $message = '') + * @method static void nullOrPropertyExists($value, $property, $message = '') + * @method static void nullOrPropertyNotExists($value, $property, $message = '') + * @method static void nullOrMethodExists($value, $method, $message = '') + * @method static void nullOrMethodNotExists($value, $method, $message = '') + * @method static void nullOrKeyExists($value, $key, $message = '') + * @method static void nullOrKeyNotExists($value, $key, $message = '') + * @method static void nullOrCount($value, $key, $message = '') + * @method static void nullOrMinCount($value, $min, $message = '') + * @method static void nullOrMaxCount($value, $max, $message = '') + * @method static void nullOrIsList($value, $message = '') + * @method static void nullOrIsMap($value, $message = '') + * @method static void nullOrCountBetween($value, $min, $max, $message = '') + * @method static void nullOrUuid($values, $message = '') + * @method static void nullOrThrows($expression, $class = 'Exception', $message = '') + * @method static void allString($values, $message = '') + * @method static void allStringNotEmpty($values, $message = '') + * @method static void allInteger($values, $message = '') + * @method static void allIntegerish($values, $message = '') + * @method static void allFloat($values, $message = '') + * @method static void allNumeric($values, $message = '') + * @method static void allNatural($values, $message = '') + * @method static void allBoolean($values, $message = '') + * @method static void allScalar($values, $message = '') + * @method static void allObject($values, $message = '') + * @method static void allResource($values, $type = null, $message = '') + * @method static void allIsCallable($values, $message = '') + * @method static void allIsArray($values, $message = '') + * @method static void allIsTraversable($values, $message = '') + * @method static void allIsArrayAccessible($values, $message = '') + * @method static void allIsCountable($values, $message = '') + * @method static void allIsIterable($values, $message = '') + * @method static void allIsInstanceOf($values, $class, $message = '') + * @method static void allNotInstanceOf($values, $class, $message = '') + * @method static void allIsInstanceOfAny($values, $classes, $message = '') + * @method static void allNull($values, $message = '') + * @method static void allNotNull($values, $message = '') + * @method static void allIsEmpty($values, $message = '') + * @method static void allNotEmpty($values, $message = '') + * @method static void allTrue($values, $message = '') + * @method static void allFalse($values, $message = '') + * @method static void allIp($values, $message = '') + * @method static void allIpv4($values, $message = '') + * @method static void allIpv6($values, $message = '') + * @method static void allEq($values, $value2, $message = '') + * @method static void allNotEq($values,$value2, $message = '') + * @method static void allSame($values, $value2, $message = '') + * @method static void allNotSame($values, $value2, $message = '') + * @method static void allGreaterThan($values, $value2, $message = '') + * @method static void allGreaterThanEq($values, $value2, $message = '') + * @method static void allLessThan($values, $value2, $message = '') + * @method static void allLessThanEq($values, $value2, $message = '') + * @method static void allRange($values, $min, $max, $message = '') + * @method static void allOneOf($values, $values, $message = '') + * @method static void allContains($values, $subString, $message = '') + * @method static void allNotContains($values, $subString, $message = '') + * @method static void allNotWhitespaceOnly($values, $message = '') + * @method static void allStartsWith($values, $prefix, $message = '') + * @method static void allStartsWithLetter($values, $message = '') + * @method static void allEndsWith($values, $suffix, $message = '') + * @method static void allRegex($values, $pattern, $message = '') + * @method static void allNotRegex($values, $pattern, $message = '') + * @method static void allAlpha($values, $message = '') + * @method static void allDigits($values, $message = '') + * @method static void allAlnum($values, $message = '') + * @method static void allLower($values, $message = '') + * @method static void allUpper($values, $message = '') + * @method static void allLength($values, $length, $message = '') + * @method static void allMinLength($values, $min, $message = '') + * @method static void allMaxLength($values, $max, $message = '') + * @method static void allLengthBetween($values, $min, $max, $message = '') + * @method static void allFileExists($values, $message = '') + * @method static void allFile($values, $message = '') + * @method static void allDirectory($values, $message = '') + * @method static void allReadable($values, $message = '') + * @method static void allWritable($values, $message = '') + * @method static void allClassExists($values, $message = '') + * @method static void allSubclassOf($values, $class, $message = '') + * @method static void allInterfaceExists($values, $message = '') + * @method static void allImplementsInterface($values, $interface, $message = '') + * @method static void allPropertyExists($values, $property, $message = '') + * @method static void allPropertyNotExists($values, $property, $message = '') + * @method static void allMethodExists($values, $method, $message = '') + * @method static void allMethodNotExists($values, $method, $message = '') + * @method static void allKeyExists($values, $key, $message = '') + * @method static void allKeyNotExists($values, $key, $message = '') + * @method static void allCount($values, $key, $message = '') + * @method static void allMinCount($values, $min, $message = '') + * @method static void allMaxCount($values, $max, $message = '') + * @method static void allCountBetween($values, $min, $max, $message = '') + * @method static void allIsList($values, $message = '') + * @method static void allIsMap($values, $message = '') + * @method static void allUuid($values, $message = '') + * @method static void allThrows($expressions, $class = 'Exception', $message = '') + * + * @since 1.0 + * + * @author Bernhard Schussek + */ +class Assert +{ + public static function string($value, $message = '') + { + if (!is_string($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a string. Got: %s', + static::typeToString($value) + )); + } + } + + public static function stringNotEmpty($value, $message = '') + { + static::string($value, $message); + static::notEq($value, '', $message); + } + + public static function integer($value, $message = '') + { + if (!is_int($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an integer. Got: %s', + static::typeToString($value) + )); + } + } + + public static function integerish($value, $message = '') + { + if (!is_numeric($value) || $value != (int) $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an integerish value. Got: %s', + static::typeToString($value) + )); + } + } + + public static function float($value, $message = '') + { + if (!is_float($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a float. Got: %s', + static::typeToString($value) + )); + } + } + + public static function numeric($value, $message = '') + { + if (!is_numeric($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a numeric. Got: %s', + static::typeToString($value) + )); + } + } + + public static function natural($value, $message = '') + { + if (!is_int($value) || $value < 0) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a non-negative integer. Got %s', + static::valueToString($value) + )); + } + } + + public static function boolean($value, $message = '') + { + if (!is_bool($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a boolean. Got: %s', + static::typeToString($value) + )); + } + } + + public static function scalar($value, $message = '') + { + if (!is_scalar($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a scalar. Got: %s', + static::typeToString($value) + )); + } + } + + public static function object($value, $message = '') + { + if (!is_object($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an object. Got: %s', + static::typeToString($value) + )); + } + } + + public static function resource($value, $type = null, $message = '') + { + if (!is_resource($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a resource. Got: %s', + static::typeToString($value) + )); + } + + if ($type && $type !== get_resource_type($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a resource of type %2$s. Got: %s', + static::typeToString($value), + $type + )); + } + } + + public static function isCallable($value, $message = '') + { + if (!is_callable($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a callable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isArray($value, $message = '') + { + if (!is_array($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isTraversable($value, $message = '') + { + @trigger_error( + sprintf( + 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', + __METHOD__ + ), + E_USER_DEPRECATED + ); + + if (!is_array($value) && !($value instanceof Traversable)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a traversable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isArrayAccessible($value, $message = '') + { + if (!is_array($value) && !($value instanceof ArrayAccess)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array accessible. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isCountable($value, $message = '') + { + if (!is_array($value) && !($value instanceof Countable)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a countable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isIterable($value, $message = '') + { + if (!is_array($value) && !($value instanceof Traversable)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an iterable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isInstanceOf($value, $class, $message = '') + { + if (!($value instanceof $class)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an instance of %2$s. Got: %s', + static::typeToString($value), + $class + )); + } + } + + public static function notInstanceOf($value, $class, $message = '') + { + if ($value instanceof $class) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an instance other than %2$s. Got: %s', + static::typeToString($value), + $class + )); + } + } + + public static function isInstanceOfAny($value, array $classes, $message = '') + { + foreach ($classes as $class) { + if ($value instanceof $class) { + return; + } + } + + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an instance of any of %2$s. Got: %s', + static::typeToString($value), + implode(', ', array_map(array('static', 'valueToString'), $classes)) + )); + } + + public static function isEmpty($value, $message = '') + { + if (!empty($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an empty value. Got: %s', + static::valueToString($value) + )); + } + } + + public static function notEmpty($value, $message = '') + { + if (empty($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a non-empty value. Got: %s', + static::valueToString($value) + )); + } + } + + public static function null($value, $message = '') + { + if (null !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected null. Got: %s', + static::valueToString($value) + )); + } + } + + public static function notNull($value, $message = '') + { + if (null === $value) { + static::reportInvalidArgument( + $message ?: 'Expected a value other than null.' + ); + } + } + + public static function true($value, $message = '') + { + if (true !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be true. Got: %s', + static::valueToString($value) + )); + } + } + + public static function false($value, $message = '') + { + if (false !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be false. Got: %s', + static::valueToString($value) + )); + } + } + + public static function ip($value, $message = '') + { + if (false === filter_var($value, FILTER_VALIDATE_IP)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be an IP. Got: %s', + static::valueToString($value) + )); + } + } + + public static function ipv4($value, $message = '') + { + if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be an IPv4. Got: %s', + static::valueToString($value) + )); + } + } + + public static function ipv6($value, $message = '') + { + if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be an IPv6. Got %s', + static::valueToString($value) + )); + } + } + + public static function eq($value, $value2, $message = '') + { + if ($value2 != $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($value2) + )); + } + } + + public static function notEq($value, $value2, $message = '') + { + if ($value2 == $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a different value than %s.', + static::valueToString($value2) + )); + } + } + + public static function same($value, $value2, $message = '') + { + if ($value2 !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value identical to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($value2) + )); + } + } + + public static function notSame($value, $value2, $message = '') + { + if ($value2 === $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value not identical to %s.', + static::valueToString($value2) + )); + } + } + + public static function greaterThan($value, $limit, $message = '') + { + if ($value <= $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value greater than %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function greaterThanEq($value, $limit, $message = '') + { + if ($value < $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value greater than or equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function lessThan($value, $limit, $message = '') + { + if ($value >= $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value less than %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function lessThanEq($value, $limit, $message = '') + { + if ($value > $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value less than or equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function range($value, $min, $max, $message = '') + { + if ($value < $min || $value > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value between %2$s and %3$s. Got: %s', + static::valueToString($value), + static::valueToString($min), + static::valueToString($max) + )); + } + } + + public static function oneOf($value, array $values, $message = '') + { + if (!in_array($value, $values, true)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected one of: %2$s. Got: %s', + static::valueToString($value), + implode(', ', array_map(array('static', 'valueToString'), $values)) + )); + } + } + + public static function contains($value, $subString, $message = '') + { + if (false === strpos($value, $subString)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain %2$s. Got: %s', + static::valueToString($value), + static::valueToString($subString) + )); + } + } + + public static function notContains($value, $subString, $message = '') + { + if (false !== strpos($value, $subString)) { + static::reportInvalidArgument(sprintf( + $message ?: '%2$s was not expected to be contained in a value. Got: %s', + static::valueToString($value), + static::valueToString($subString) + )); + } + } + + public static function notWhitespaceOnly($value, $message = '') + { + if (preg_match('/^\s*$/', $value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a non-whitespace string. Got: %s', + static::valueToString($value) + )); + } + } + + public static function startsWith($value, $prefix, $message = '') + { + if (0 !== strpos($value, $prefix)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to start with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($prefix) + )); + } + } + + public static function startsWithLetter($value, $message = '') + { + $valid = isset($value[0]); + + if ($valid) { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = ctype_alpha($value[0]); + setlocale(LC_CTYPE, $locale); + } + + if (!$valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to start with a letter. Got: %s', + static::valueToString($value) + )); + } + } + + public static function endsWith($value, $suffix, $message = '') + { + if ($suffix !== substr($value, -static::strlen($suffix))) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to end with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($suffix) + )); + } + } + + public static function regex($value, $pattern, $message = '') + { + if (!preg_match($pattern, $value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The value %s does not match the expected pattern.', + static::valueToString($value) + )); + } + } + + public static function notRegex($value, $pattern, $message = '') + { + if (preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The value %s matches the pattern %s (at offset %d).', + static::valueToString($value), + static::valueToString($pattern), + $matches[0][1] + )); + } + } + + public static function alpha($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_alpha($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain only letters. Got: %s', + static::valueToString($value) + )); + } + } + + public static function digits($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_digit($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain digits only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function alnum($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_alnum($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain letters and digits only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function lower($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_lower($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain lowercase characters only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function upper($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_upper($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain uppercase characters only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function length($value, $length, $message = '') + { + if ($length !== static::strlen($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain %2$s characters. Got: %s', + static::valueToString($value), + $length + )); + } + } + + public static function minLength($value, $min, $message = '') + { + if (static::strlen($value) < $min) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain at least %2$s characters. Got: %s', + static::valueToString($value), + $min + )); + } + } + + public static function maxLength($value, $max, $message = '') + { + if (static::strlen($value) > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain at most %2$s characters. Got: %s', + static::valueToString($value), + $max + )); + } + } + + public static function lengthBetween($value, $min, $max, $message = '') + { + $length = static::strlen($value); + + if ($length < $min || $length > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', + static::valueToString($value), + $min, + $max + )); + } + } + + public static function fileExists($value, $message = '') + { + static::string($value); + + if (!file_exists($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The file %s does not exist.', + static::valueToString($value) + )); + } + } + + public static function file($value, $message = '') + { + static::fileExists($value, $message); + + if (!is_file($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is not a file.', + static::valueToString($value) + )); + } + } + + public static function directory($value, $message = '') + { + static::fileExists($value, $message); + + if (!is_dir($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is no directory.', + static::valueToString($value) + )); + } + } + + public static function readable($value, $message = '') + { + if (!is_readable($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is not readable.', + static::valueToString($value) + )); + } + } + + public static function writable($value, $message = '') + { + if (!is_writable($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is not writable.', + static::valueToString($value) + )); + } + } + + public static function classExists($value, $message = '') + { + if (!class_exists($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an existing class name. Got: %s', + static::valueToString($value) + )); + } + } + + public static function subclassOf($value, $class, $message = '') + { + if (!is_subclass_of($value, $class)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a sub-class of %2$s. Got: %s', + static::valueToString($value), + static::valueToString($class) + )); + } + } + + public static function interfaceExists($value, $message = '') + { + if (!interface_exists($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an existing interface name. got %s', + static::valueToString($value) + )); + } + } + + public static function implementsInterface($value, $interface, $message = '') + { + if (!in_array($interface, class_implements($value))) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an implementation of %2$s. Got: %s', + static::valueToString($value), + static::valueToString($interface) + )); + } + } + + public static function propertyExists($classOrObject, $property, $message = '') + { + if (!property_exists($classOrObject, $property)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the property %s to exist.', + static::valueToString($property) + )); + } + } + + public static function propertyNotExists($classOrObject, $property, $message = '') + { + if (property_exists($classOrObject, $property)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the property %s to not exist.', + static::valueToString($property) + )); + } + } + + public static function methodExists($classOrObject, $method, $message = '') + { + if (!method_exists($classOrObject, $method)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the method %s to exist.', + static::valueToString($method) + )); + } + } + + public static function methodNotExists($classOrObject, $method, $message = '') + { + if (method_exists($classOrObject, $method)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the method %s to not exist.', + static::valueToString($method) + )); + } + } + + public static function keyExists($array, $key, $message = '') + { + if (!(isset($array[$key]) || array_key_exists($key, $array))) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the key %s to exist.', + static::valueToString($key) + )); + } + } + + public static function keyNotExists($array, $key, $message = '') + { + if (isset($array[$key]) || array_key_exists($key, $array)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the key %s to not exist.', + static::valueToString($key) + )); + } + } + + public static function count($array, $number, $message = '') + { + static::eq( + count($array), + $number, + $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array)) + ); + } + + public static function minCount($array, $min, $message = '') + { + if (count($array) < $min) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array to contain at least %2$d elements. Got: %d', + count($array), + $min + )); + } + } + + public static function maxCount($array, $max, $message = '') + { + if (count($array) > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array to contain at most %2$d elements. Got: %d', + count($array), + $max + )); + } + } + + public static function countBetween($array, $min, $max, $message = '') + { + $count = count($array); + + if ($count < $min || $count > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', + $count, + $min, + $max + )); + } + } + + public static function isList($array, $message = '') + { + if (!is_array($array) || !$array || array_keys($array) !== range(0, count($array) - 1)) { + static::reportInvalidArgument( + $message ?: 'Expected list - non-associative array.' + ); + } + } + + public static function isMap($array, $message = '') + { + if ( + !is_array($array) || + !$array || + array_keys($array) !== array_filter(array_keys($array), function ($key) { + return is_string($key); + }) + ) { + static::reportInvalidArgument( + $message ?: 'Expected map - associative array with string keys.' + ); + } + } + + public static function uuid($value, $message = '') + { + $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); + + // The nil UUID is special form of UUID that is specified to have all + // 128 bits set to zero. + if ('00000000-0000-0000-0000-000000000000' === $value) { + return; + } + + if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Value %s is not a valid UUID.', + static::valueToString($value) + )); + } + } + + public static function throws(Closure $expression, $class = 'Exception', $message = '') + { + static::string($class); + + $actual = 'none'; + + try { + $expression(); + } catch (Exception $e) { + $actual = get_class($e); + if ($e instanceof $class) { + return; + } + } catch (Throwable $e) { + $actual = get_class($e); + if ($e instanceof $class) { + return; + } + } + + static::reportInvalidArgument($message ?: sprintf( + 'Expected to throw "%s", got "%s"', + $class, + $actual + )); + } + + public static function __callStatic($name, $arguments) + { + if ('nullOr' === substr($name, 0, 6)) { + if (null !== $arguments[0]) { + $method = lcfirst(substr($name, 6)); + call_user_func_array(array('static', $method), $arguments); + } + + return; + } + + if ('all' === substr($name, 0, 3)) { + static::isIterable($arguments[0]); + + $method = lcfirst(substr($name, 3)); + $args = $arguments; + + foreach ($arguments[0] as $entry) { + $args[0] = $entry; + + call_user_func_array(array('static', $method), $args); + } + + return; + } + + throw new BadMethodCallException('No such method: '.$name); + } + + protected static function valueToString($value) + { + if (null === $value) { + return 'null'; + } + + if (true === $value) { + return 'true'; + } + + if (false === $value) { + return 'false'; + } + + if (is_array($value)) { + return 'array'; + } + + if (is_object($value)) { + if (method_exists($value, '__toString')) { + return get_class($value).': '.self::valueToString($value->__toString()); + } + + return get_class($value); + } + + if (is_resource($value)) { + return 'resource'; + } + + if (is_string($value)) { + return '"'.$value.'"'; + } + + return (string) $value; + } + + protected static function typeToString($value) + { + return is_object($value) ? get_class($value) : gettype($value); + } + + protected static function strlen($value) + { + if (!function_exists('mb_detect_encoding')) { + return strlen($value); + } + + if (false === $encoding = mb_detect_encoding($value)) { + return strlen($value); + } + + return mb_strwidth($value, $encoding); + } + + protected static function reportInvalidArgument($message) + { + throw new InvalidArgumentException($message); + } + + private function __construct() + { + } +} +The MIT License (MIT) + +Copyright (c) 2014 Bernhard Schussek + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Environment; + +final class Console +{ + /** + * @var int + */ + public const STDIN = 0; + + /** + * @var int + */ + public const STDOUT = 1; + + /** + * @var int + */ + public const STDERR = 2; + + /** + * Returns true if STDOUT supports colorization. + * + * This code has been copied and adapted from + * Symfony\Component\Console\Output\StreamOutput. + */ + public function hasColorSupport(): bool + { + if ('Hyper' === \getenv('TERM_PROGRAM')) { + return true; + } + + if ($this->isWindows()) { + // @codeCoverageIgnoreStart + return (\defined('STDOUT') && \function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(\STDOUT)) + || false !== \getenv('ANSICON') + || 'ON' === \getenv('ConEmuANSI') + || 'xterm' === \getenv('TERM'); + // @codeCoverageIgnoreEnd + } + + if (!\defined('STDOUT')) { + // @codeCoverageIgnoreStart + return false; + // @codeCoverageIgnoreEnd + } + + if ($this->isInteractive(\STDOUT)) { + return true; + } + + $stat = @\fstat(\STDOUT); + // Check if formatted mode is S_IFCHR + return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; + } + + /** + * Returns the number of columns of the terminal. + * + * @codeCoverageIgnore + */ + public function getNumberOfColumns(): int + { + if ($this->isWindows()) { + return $this->getNumberOfColumnsWindows(); + } + + if (!$this->isInteractive(\defined('STDIN') ? \STDIN : self::STDIN)) { + return 80; + } + + return $this->getNumberOfColumnsInteractive(); + } + + /** + * Returns if the file descriptor is an interactive terminal or not. + * + * Normally, we want to use a resource as a parameter, yet sadly it's not always awailable, + * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. + * + * @param int|resource $fileDescriptor + */ + public function isInteractive($fileDescriptor = self::STDOUT): bool + { + return (\is_resource($fileDescriptor) && \function_exists('stream_isatty') && @\stream_isatty($fileDescriptor)) // stream_isatty requires that descriptor is a real resource, not numeric ID of it + || (\function_exists('posix_isatty') && @\posix_isatty($fileDescriptor)); + } + + private function isWindows(): bool + { + return \DIRECTORY_SEPARATOR === '\\'; + } + + /** + * @codeCoverageIgnore + */ + private function getNumberOfColumnsInteractive(): int + { + if (\function_exists('shell_exec') && \preg_match('#\d+ (\d+)#', \shell_exec('stty size') ?? '', $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } + } + + if (\function_exists('shell_exec') && \preg_match('#columns = (\d+);#', \shell_exec('stty') ?? '', $match) === 1) { + if ((int) $match[1] > 0) { + return (int) $match[1]; + } + } + + return 80; + } + + /** + * @codeCoverageIgnore + */ + private function getNumberOfColumnsWindows(): int + { + $ansicon = \getenv('ANSICON'); + $columns = 80; + + if (\is_string($ansicon) && \preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', \trim($ansicon), $matches)) { + $columns = $matches[1]; + } elseif (\function_exists('proc_open')) { + $process = \proc_open( + 'mode CON', + [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ], + $pipes, + null, + null, + ['suppress_errors' => true] + ); + + if (\is_resource($process)) { + $info = \stream_get_contents($pipes[1]); + + \fclose($pipes[1]); + \fclose($pipes[2]); + \proc_close($process); + + if (\preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { + $columns = $matches[2]; + } + } + } + + return $columns - 1; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Environment; + +/** + * Utility class for HHVM/PHP environment handling. + */ +final class Runtime +{ + /** + * @var string + */ + private static $binary; + + /** + * Returns true when Xdebug or PCOV is available or + * the runtime used is PHPDBG. + */ + public function canCollectCodeCoverage(): bool + { + return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); + } + + /** + * Returns true when Zend OPcache is loaded, enabled, and is configured to discard comments. + */ + public function discardsComments(): bool + { + if (!\extension_loaded('Zend OPcache')) { + return false; + } + + if (\ini_get('opcache.save_comments') !== '0') { + return false; + } + + if (\PHP_SAPI === 'cli' && \ini_get('opcache.enable_cli') === '1') { + return true; + } + + if (\PHP_SAPI !== 'cli' && \ini_get('opcache.enable') === '1') { + return true; + } + + return false; + } + + /** + * Returns the path to the binary of the current runtime. + * Appends ' --php' to the path when the runtime is HHVM. + */ + public function getBinary(): string + { + // HHVM + if (self::$binary === null && $this->isHHVM()) { + // @codeCoverageIgnoreStart + if ((self::$binary = \getenv('PHP_BINARY')) === false) { + self::$binary = \PHP_BINARY; + } + + self::$binary = \escapeshellarg(self::$binary) . ' --php' . + ' -d hhvm.php7.all=1'; + // @codeCoverageIgnoreEnd + } + + if (self::$binary === null && \PHP_BINARY !== '') { + self::$binary = \escapeshellarg(\PHP_BINARY); + } + + if (self::$binary === null) { + // @codeCoverageIgnoreStart + $possibleBinaryLocations = [ + \PHP_BINDIR . '/php', + \PHP_BINDIR . '/php-cli.exe', + \PHP_BINDIR . '/php.exe', + ]; + + foreach ($possibleBinaryLocations as $binary) { + if (\is_readable($binary)) { + self::$binary = \escapeshellarg($binary); + + break; + } + } + // @codeCoverageIgnoreEnd + } + + if (self::$binary === null) { + // @codeCoverageIgnoreStart + self::$binary = 'php'; + // @codeCoverageIgnoreEnd + } + + return self::$binary; + } + + public function getNameWithVersion(): string + { + return $this->getName() . ' ' . $this->getVersion(); + } + + public function getNameWithVersionAndCodeCoverageDriver(): string + { + if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { + return $this->getNameWithVersion(); + } + + if ($this->hasXdebug()) { + return \sprintf( + '%s with Xdebug %s', + $this->getNameWithVersion(), + \phpversion('xdebug') + ); + } + + if ($this->hasPCOV()) { + return \sprintf( + '%s with PCOV %s', + $this->getNameWithVersion(), + \phpversion('pcov') + ); + } + } + + public function getName(): string + { + if ($this->isHHVM()) { + // @codeCoverageIgnoreStart + return 'HHVM'; + // @codeCoverageIgnoreEnd + } + + if ($this->isPHPDBG()) { + // @codeCoverageIgnoreStart + return 'PHPDBG'; + // @codeCoverageIgnoreEnd + } + + return 'PHP'; + } + + public function getVendorUrl(): string + { + if ($this->isHHVM()) { + // @codeCoverageIgnoreStart + return 'http://hhvm.com/'; + // @codeCoverageIgnoreEnd + } + + return 'https://secure.php.net/'; + } + + public function getVersion(): string + { + if ($this->isHHVM()) { + // @codeCoverageIgnoreStart + return HHVM_VERSION; + // @codeCoverageIgnoreEnd + } + + return \PHP_VERSION; + } + + /** + * Returns true when the runtime used is PHP and Xdebug is loaded. + */ + public function hasXdebug(): bool + { + return ($this->isPHP() || $this->isHHVM()) && \extension_loaded('xdebug'); + } + + /** + * Returns true when the runtime used is HHVM. + */ + public function isHHVM(): bool + { + return \defined('HHVM_VERSION'); + } + + /** + * Returns true when the runtime used is PHP without the PHPDBG SAPI. + */ + public function isPHP(): bool + { + return !$this->isHHVM() && !$this->isPHPDBG(); + } + + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI. + */ + public function isPHPDBG(): bool + { + return \PHP_SAPI === 'phpdbg' && !$this->isHHVM(); + } + + /** + * Returns true when the runtime used is PHP with the PHPDBG SAPI + * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). + * + * @codeCoverageIgnore + */ + public function hasPHPDBGCodeCoverage(): bool + { + return $this->isPHPDBG(); + } + + /** + * Returns true when the runtime used is PHP with PCOV loaded and enabled + */ + public function hasPCOV(): bool + { + return $this->isPHP() && \extension_loaded('pcov') && \ini_get('pcov.enabled'); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\Environment; + +final class OperatingSystem +{ + /** + * Returns PHP_OS_FAMILY (if defined (which it is on PHP >= 7.2)). + * Returns a string (compatible with PHP_OS_FAMILY) derived from PHP_OS otherwise. + */ + public function getFamily(): string + { + if (\defined('PHP_OS_FAMILY')) { + return \PHP_OS_FAMILY; + } + + if (\DIRECTORY_SEPARATOR === '\\') { + return 'Windows'; + } + + switch (\PHP_OS) { + case 'Darwin': + return 'Darwin'; + + case 'DragonFly': + case 'FreeBSD': + case 'NetBSD': + case 'OpenBSD': + return 'BSD'; + + case 'Linux': + return 'Linux'; + + case 'SunOS': + return 'Solaris'; + + default: + return 'Unknown'; + } + } +} +sebastian/environment + +Copyright (c) 2014-2019, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\ObjectEnumerator; + +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\ObjectEnumerator; + +use SebastianBergmann\ObjectReflector\ObjectReflector; +use SebastianBergmann\RecursionContext\Context; + +/** + * Traverses array structures and object graphs + * to enumerate all referenced objects. + */ +class Enumerator +{ + /** + * Returns an array of all objects referenced either + * directly or indirectly by a variable. + * + * @param array|object $variable + * + * @return object[] + */ + public function enumerate($variable) + { + if (!is_array($variable) && !is_object($variable)) { + throw new InvalidArgumentException; + } + + if (isset(func_get_args()[1])) { + if (!func_get_args()[1] instanceof Context) { + throw new InvalidArgumentException; + } + + $processed = func_get_args()[1]; + } else { + $processed = new Context; + } + + $objects = []; + + if ($processed->contains($variable)) { + return $objects; + } + + $array = $variable; + $processed->add($variable); + + if (is_array($variable)) { + foreach ($array as $element) { + if (!is_array($element) && !is_object($element)) { + continue; + } + + $objects = array_merge( + $objects, + $this->enumerate($element, $processed) + ); + } + } else { + $objects[] = $variable; + + $reflector = new ObjectReflector; + + foreach ($reflector->getAttributes($variable) as $value) { + if (!is_array($value) && !is_object($value)) { + continue; + } + + $objects = array_merge( + $objects, + $this->enumerate($value, $processed) + ); + } + } + + return $objects; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\ObjectEnumerator; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann; + +/** + * @since Class available since Release 1.0.0 + */ +class Version +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $release; + + /** + * @var string + */ + private $version; + + /** + * @param string $release + * @param string $path + */ + public function __construct($release, $path) + { + $this->release = $release; + $this->path = $path; + } + + /** + * @return string + */ + public function getVersion() + { + if ($this->version === null) { + if (count(explode('.', $this->release)) == 3) { + $this->version = $this->release; + } else { + $this->version = $this->release . '-dev'; + } + + $git = $this->getGitInformation($this->path); + + if ($git) { + if (count(explode('.', $this->release)) == 3) { + $this->version = $git; + } else { + $git = explode('-', $git); + + $this->version = $this->release . '-' . end($git); + } + } + } + + return $this->version; + } + + /** + * @param string $path + * + * @return bool|string + */ + private function getGitInformation($path) + { + if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) { + return false; + } + + $process = proc_open( + 'git describe --tags', + [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ], + $pipes, + $path + ); + + if (!is_resource($process)) { + return false; + } + + $result = trim(stream_get_contents($pipes[1])); + + fclose($pipes[1]); + fclose($pipes[2]); + + $returnCode = proc_close($process); + + if ($returnCode !== 0) { + return false; + } + + return $result; + } +} +Version + +Copyright (c) 2013-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +final class ConfigurationException extends InvalidArgumentException +{ + /** + * @param string $option + * @param string $expected + * @param mixed $value + * @param int $code + * @param null|\Exception $previous + */ + public function __construct( + string $option, + string $expected, + $value, + int $code = 0, + \Exception $previous = null + ) { + parent::__construct( + \sprintf( + 'Option "%s" must be %s, got "%s".', + $option, + $expected, + \is_object($value) ? \get_class($value) : (null === $value ? '' : \gettype($value) . '#' . $value) + ), + $code, + $previous + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ + /** + * {@inheritdoc} + */ + public function calculate(array $from, array $to): array + { + $cFrom = \count($from); + $cTo = \count($to); + + if ($cFrom === 0) { + return []; + } + + if ($cFrom === 1) { + if (\in_array($from[0], $to, true)) { + return [$from[0]]; + } + + return []; + } + + $i = (int) ($cFrom / 2); + $fromStart = \array_slice($from, 0, $i); + $fromEnd = \array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); + $jMax = 0; + $max = 0; + + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + + if ($m >= $max) { + $max = $m; + $jMax = $j; + } + } + + $toStart = \array_slice($to, 0, $jMax); + $toEnd = \array_slice($to, $jMax); + + return \array_merge( + $this->calculate($fromStart, $toStart), + $this->calculate($fromEnd, $toEnd) + ); + } + + private function length(array $from, array $to): array + { + $current = \array_fill(0, \count($to) + 1, 0); + $cFrom = \count($from); + $cTo = \count($to); + + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] === $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = \max($current[$j], $prev[$j + 1]); + } + } + } + + return $current; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator +{ + /** + * {@inheritdoc} + */ + public function calculate(array $from, array $to): array + { + $common = []; + $fromLength = \count($from); + $toLength = \count($to); + $width = $fromLength + 1; + $matrix = new \SplFixedArray($width * ($toLength + 1)); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = ($j * $width) + $i; + $matrix[$o] = \max( + $matrix[$o - 1], + $matrix[$o - $width], + $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i - 1] === $to[$j - 1]) { + $common[] = $from[$i - 1]; + --$i; + --$j; + } else { + $o = ($j * $width) + $i; + + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } + } + + return \array_reverse($common); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +final class Line +{ + public const ADDED = 1; + public const REMOVED = 2; + public const UNCHANGED = 3; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $content; + + public function __construct(int $type = self::UNCHANGED, string $content = '') + { + $this->type = $type; + $this->content = $content; + } + + public function getContent(): string + { + return $this->content; + } + + public function getType(): int + { + return $this->type; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; +use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; + +/** + * Diff implementation. + */ +final class Differ +{ + public const OLD = 0; + public const ADDED = 1; + public const REMOVED = 2; + public const DIFF_LINE_END_WARNING = 3; + public const NO_LINE_END_EOF_WARNING = 4; + + /** + * @var DiffOutputBuilderInterface + */ + private $outputBuilder; + + /** + * @param DiffOutputBuilderInterface $outputBuilder + * + * @throws InvalidArgumentException + */ + public function __construct($outputBuilder = null) + { + if ($outputBuilder instanceof DiffOutputBuilderInterface) { + $this->outputBuilder = $outputBuilder; + } elseif (null === $outputBuilder) { + $this->outputBuilder = new UnifiedDiffOutputBuilder; + } elseif (\is_string($outputBuilder)) { + // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support + // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 + // @deprecated + $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); + } else { + throw new InvalidArgumentException( + \sprintf( + 'Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', + \is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"' + ) + ); + } + } + + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @param null|LongestCommonSubsequenceCalculator $lcs + * + * @return string + */ + public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string + { + $diff = $this->diffToArray( + $this->normalizeDiffInput($from), + $this->normalizeDiffInput($to), + $lcs + ); + + return $this->outputBuilder->getDiff($diff); + } + + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => mixed $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequenceCalculator $lcs + * + * @return array + */ + public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array + { + if (\is_string($from)) { + $from = $this->splitStringByLines($from); + } elseif (!\is_array($from)) { + throw new InvalidArgumentException('"from" must be an array or string.'); + } + + if (\is_string($to)) { + $to = $this->splitStringByLines($to); + } elseif (!\is_array($to)) { + throw new InvalidArgumentException('"to" must be an array or string.'); + } + + [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); + + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + + $common = $lcs->calculate(\array_values($from), \array_values($to)); + $diff = []; + + foreach ($start as $token) { + $diff[] = [$token, self::OLD]; + } + + \reset($from); + \reset($to); + + foreach ($common as $token) { + while (($fromToken = \reset($from)) !== $token) { + $diff[] = [\array_shift($from), self::REMOVED]; + } + + while (($toToken = \reset($to)) !== $token) { + $diff[] = [\array_shift($to), self::ADDED]; + } + + $diff[] = [$token, self::OLD]; + + \array_shift($from); + \array_shift($to); + } + + while (($token = \array_shift($from)) !== null) { + $diff[] = [$token, self::REMOVED]; + } + + while (($token = \array_shift($to)) !== null) { + $diff[] = [$token, self::ADDED]; + } + + foreach ($end as $token) { + $diff[] = [$token, self::OLD]; + } + + if ($this->detectUnmatchedLineEndings($diff)) { + \array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); + } + + return $diff; + } + + /** + * Casts variable to string if it is not a string or array. + * + * @param mixed $input + * + * @return array|string + */ + private function normalizeDiffInput($input) + { + if (!\is_array($input) && !\is_string($input)) { + return (string) $input; + } + + return $input; + } + + /** + * Checks if input is string, if so it will split it line-by-line. + * + * @param string $input + * + * @return array + */ + private function splitStringByLines(string $input): array + { + return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + } + + /** + * @param array $from + * @param array $to + * + * @return LongestCommonSubsequenceCalculator + */ + private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientLongestCommonSubsequenceCalculator; + } + + return new TimeEfficientLongestCommonSubsequenceCalculator; + } + + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @param array $from + * @param array $to + * + * @return float|int + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; + + return $itemSize * \min(\count($from), \count($to)) ** 2; + } + + /** + * Returns true if line ends don't match in a diff. + * + * @param array $diff + * + * @return bool + */ + private function detectUnmatchedLineEndings(array $diff): bool + { + $newLineBreaks = ['' => true]; + $oldLineBreaks = ['' => true]; + + foreach ($diff as $entry) { + if (self::OLD === $entry[1]) { + $ln = $this->getLinebreak($entry[0]); + $oldLineBreaks[$ln] = true; + $newLineBreaks[$ln] = true; + } elseif (self::ADDED === $entry[1]) { + $newLineBreaks[$this->getLinebreak($entry[0])] = true; + } elseif (self::REMOVED === $entry[1]) { + $oldLineBreaks[$this->getLinebreak($entry[0])] = true; + } + } + + // if either input or output is a single line without breaks than no warning should be raised + if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { + return false; + } + + // two way compare + foreach ($newLineBreaks as $break => $set) { + if (!isset($oldLineBreaks[$break])) { + return true; + } + } + + foreach ($oldLineBreaks as $break => $set) { + if (!isset($newLineBreaks[$break])) { + return true; + } + } + + return false; + } + + private function getLinebreak($line): string + { + if (!\is_string($line)) { + return ''; + } + + $lc = \substr($line, -1); + + if ("\r" === $lc) { + return "\r"; + } + + if ("\n" !== $lc) { + return ''; + } + + if ("\r\n" === \substr($line, -2)) { + return "\r\n"; + } + + return "\n"; + } + + private static function getArrayDiffParted(array &$from, array &$to): array + { + $start = []; + $end = []; + + \reset($to); + + foreach ($from as $k => $v) { + $toK = \key($to); + + if ($toK === $k && $v === $to[$k]) { + $start[$k] = $v; + + unset($from[$k], $to[$k]); + } else { + break; + } + } + + \end($from); + \end($to); + + do { + $fromK = \key($from); + $toK = \key($to); + + if (null === $fromK || null === $toK || \current($from) !== \current($to)) { + break; + } + + \prev($from); + \prev($to); + + $end = [$fromK => $from[$fromK]] + $end; + unset($from[$fromK], $to[$toK]); + } while (true); + + return [$from, $to, $start, $end]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\Output; + +/** + * Defines how an output builder should take a generated + * diff array and return a string representation of that diff. + */ +interface DiffOutputBuilderInterface +{ + public function getDiff(array $diff): string; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\Output; + +use SebastianBergmann\Diff\ConfigurationException; +use SebastianBergmann\Diff\Differ; + +/** + * Strict Unified diff output builder. + * + * Generates (strict) Unified diff's (unidiffs) with hunks. + */ +final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface +{ + private static $default = [ + 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` + 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) + 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 + 'fromFile' => null, + 'fromFileDate' => null, + 'toFile' => null, + 'toFileDate' => null, + ]; + /** + * @var bool + */ + private $changed; + + /** + * @var bool + */ + private $collapseRanges; + + /** + * @var int >= 0 + */ + private $commonLineThreshold; + + /** + * @var string + */ + private $header; + + /** + * @var int >= 0 + */ + private $contextLines; + + public function __construct(array $options = []) + { + $options = \array_merge(self::$default, $options); + + if (!\is_bool($options['collapseRanges'])) { + throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); + } + + if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { + throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); + } + + if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { + throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); + } + + foreach (['fromFile', 'toFile'] as $option) { + if (!\is_string($options[$option])) { + throw new ConfigurationException($option, 'a string', $options[$option]); + } + } + + foreach (['fromFileDate', 'toFileDate'] as $option) { + if (null !== $options[$option] && !\is_string($options[$option])) { + throw new ConfigurationException($option, 'a string or ', $options[$option]); + } + } + + $this->header = \sprintf( + "--- %s%s\n+++ %s%s\n", + $options['fromFile'], + null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], + $options['toFile'], + null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] + ); + + $this->collapseRanges = $options['collapseRanges']; + $this->commonLineThreshold = $options['commonLineThreshold']; + $this->contextLines = $options['contextLines']; + } + + public function getDiff(array $diff): string + { + if (0 === \count($diff)) { + return ''; + } + + $this->changed = false; + + $buffer = \fopen('php://memory', 'r+b'); + \fwrite($buffer, $this->header); + + $this->writeDiffHunks($buffer, $diff); + + if (!$this->changed) { + \fclose($buffer); + + return ''; + } + + $diff = \stream_get_contents($buffer, -1, 0); + + \fclose($buffer); + + // If the last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = \substr($diff, -1); + + return "\n" !== $last && "\r" !== $last + ? $diff . "\n" + : $diff + ; + } + + private function writeDiffHunks($output, array $diff): void + { + // detect "No newline at end of file" and insert into `$diff` if needed + + $upperLimit = \count($diff); + + if (0 === $diff[$upperLimit - 1][1]) { + $lc = \substr($diff[$upperLimit - 1][0], -1); + + if ("\n" !== $lc) { + \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => true, 2 => true]; + + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = \substr($diff[$i][0], -1); + + if ("\n" !== $lc) { + \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + + if (!\count($toFind)) { + break; + } + } + } + } + + // write hunks to output buffer + + $cutOff = \max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { // same + if (false === $hunkCapture) { + ++$fromStart; + ++$toStart; + + continue; + } + + ++$sameCount; + ++$toRange; + ++$fromRange; + + if ($sameCount === $cutOff) { + $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 + ? $hunkCapture + : $this->contextLines + ; + + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $cutOff + $this->contextLines + 1, + $fromStart - $contextStartOffset, + $fromRange - $cutOff + $contextStartOffset + $this->contextLines, + $toStart - $contextStartOffset, + $toRange - $cutOff + $contextStartOffset + $this->contextLines, + $output + ); + + $fromStart += $fromRange; + $toStart += $toRange; + + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + } + + continue; + } + + $sameCount = 0; + + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + + $this->changed = true; + + if (false === $hunkCapture) { + $hunkCapture = $i; + } + + if (Differ::ADDED === $entry[1]) { // added + ++$toRange; + } + + if (Differ::REMOVED === $entry[1]) { // removed + ++$fromRange; + } + } + + if (false === $hunkCapture) { + return; + } + + // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + + $contextStartOffset = $hunkCapture - $this->contextLines < 0 + ? $hunkCapture + : $this->contextLines + ; + + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = \min($sameCount, $this->contextLines); + + $fromRange -= $sameCount; + $toRange -= $sameCount; + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $sameCount + $contextEndOffset + 1, + $fromStart - $contextStartOffset, + $fromRange + $contextStartOffset + $contextEndOffset, + $toStart - $contextStartOffset, + $toRange + $contextStartOffset + $contextEndOffset, + $output + ); + } + + private function writeHunk( + array $diff, + int $diffStartIndex, + int $diffEndIndex, + int $fromStart, + int $fromRange, + int $toStart, + int $toRange, + $output + ): void { + \fwrite($output, '@@ -' . $fromStart); + + if (!$this->collapseRanges || 1 !== $fromRange) { + \fwrite($output, ',' . $fromRange); + } + + \fwrite($output, ' +' . $toStart); + + if (!$this->collapseRanges || 1 !== $toRange) { + \fwrite($output, ',' . $toRange); + } + + \fwrite($output, " @@\n"); + + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === Differ::ADDED) { + $this->changed = true; + \fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + $this->changed = true; + \fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + \fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + $this->changed = true; + \fwrite($output, $diff[$i][0]); + } + //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package + // skip + //} else { + // unknown/invalid + //} + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\Output; + +abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface +{ + /** + * Takes input of the diff array and returns the common parts. + * Iterates through diff line by line. + * + * @param array $diff + * @param int $lineThreshold + * + * @return array + */ + protected function getCommonChunks(array $diff, int $lineThreshold = 5): array + { + $diffSize = \count($diff); + $capturing = false; + $chunkStart = 0; + $chunkSize = 0; + $commonChunks = []; + + for ($i = 0; $i < $diffSize; ++$i) { + if ($diff[$i][1] === 0 /* OLD */) { + if ($capturing === false) { + $capturing = true; + $chunkStart = $i; + $chunkSize = 0; + } else { + ++$chunkSize; + } + } elseif ($capturing !== false) { + if ($chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + + $capturing = false; + } + } + + if ($capturing !== false && $chunkSize >= $lineThreshold) { + $commonChunks[$chunkStart] = $chunkStart + $chunkSize; + } + + return $commonChunks; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\Output; + +use SebastianBergmann\Diff\Differ; + +/** + * Builds a diff string representation in unified diff format in chunks. + */ +final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder +{ + /** + * @var bool + */ + private $collapseRanges = true; + + /** + * @var int >= 0 + */ + private $commonLineThreshold = 6; + + /** + * @var int >= 0 + */ + private $contextLines = 3; + + /** + * @var string + */ + private $header; + + /** + * @var bool + */ + private $addLineNumbers; + + public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false) + { + $this->header = $header; + $this->addLineNumbers = $addLineNumbers; + } + + public function getDiff(array $diff): string + { + $buffer = \fopen('php://memory', 'r+b'); + + if ('' !== $this->header) { + \fwrite($buffer, $this->header); + + if ("\n" !== \substr($this->header, -1, 1)) { + \fwrite($buffer, "\n"); + } + } + + if (0 !== \count($diff)) { + $this->writeDiffHunks($buffer, $diff); + } + + $diff = \stream_get_contents($buffer, -1, 0); + + \fclose($buffer); + + // If the last char is not a linebreak: add it. + // This might happen when both the `from` and `to` do not have a trailing linebreak + $last = \substr($diff, -1); + + return "\n" !== $last && "\r" !== $last + ? $diff . "\n" + : $diff + ; + } + + private function writeDiffHunks($output, array $diff): void + { + // detect "No newline at end of file" and insert into `$diff` if needed + + $upperLimit = \count($diff); + + if (0 === $diff[$upperLimit - 1][1]) { + $lc = \substr($diff[$upperLimit - 1][0], -1); + + if ("\n" !== $lc) { + \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + } else { + // search back for the last `+` and `-` line, + // check if has trailing linebreak, else add under it warning under it + $toFind = [1 => true, 2 => true]; + + for ($i = $upperLimit - 1; $i >= 0; --$i) { + if (isset($toFind[$diff[$i][1]])) { + unset($toFind[$diff[$i][1]]); + $lc = \substr($diff[$i][0], -1); + + if ("\n" !== $lc) { + \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); + } + + if (!\count($toFind)) { + break; + } + } + } + } + + // write hunks to output buffer + + $cutOff = \max($this->commonLineThreshold, $this->contextLines); + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + $toStart = $fromStart = 1; + + foreach ($diff as $i => $entry) { + if (0 === $entry[1]) { // same + if (false === $hunkCapture) { + ++$fromStart; + ++$toStart; + + continue; + } + + ++$sameCount; + ++$toRange; + ++$fromRange; + + if ($sameCount === $cutOff) { + $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 + ? $hunkCapture + : $this->contextLines + ; + + // note: $contextEndOffset = $this->contextLines; + // + // because we never go beyond the end of the diff. + // with the cutoff/contextlines here the follow is never true; + // + // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { + // $contextEndOffset = count($diff) - 1; + // } + // + // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $cutOff + $this->contextLines + 1, + $fromStart - $contextStartOffset, + $fromRange - $cutOff + $contextStartOffset + $this->contextLines, + $toStart - $contextStartOffset, + $toRange - $cutOff + $contextStartOffset + $this->contextLines, + $output + ); + + $fromStart += $fromRange; + $toStart += $toRange; + + $hunkCapture = false; + $sameCount = $toRange = $fromRange = 0; + } + + continue; + } + + $sameCount = 0; + + if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { + continue; + } + + if (false === $hunkCapture) { + $hunkCapture = $i; + } + + if (Differ::ADDED === $entry[1]) { + ++$toRange; + } + + if (Differ::REMOVED === $entry[1]) { + ++$fromRange; + } + } + + if (false === $hunkCapture) { + return; + } + + // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, + // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold + + $contextStartOffset = $hunkCapture - $this->contextLines < 0 + ? $hunkCapture + : $this->contextLines + ; + + // prevent trying to write out more common lines than there are in the diff _and_ + // do not write more than configured through the context lines + $contextEndOffset = \min($sameCount, $this->contextLines); + + $fromRange -= $sameCount; + $toRange -= $sameCount; + + $this->writeHunk( + $diff, + $hunkCapture - $contextStartOffset, + $i - $sameCount + $contextEndOffset + 1, + $fromStart - $contextStartOffset, + $fromRange + $contextStartOffset + $contextEndOffset, + $toStart - $contextStartOffset, + $toRange + $contextStartOffset + $contextEndOffset, + $output + ); + } + + private function writeHunk( + array $diff, + int $diffStartIndex, + int $diffEndIndex, + int $fromStart, + int $fromRange, + int $toStart, + int $toRange, + $output + ): void { + if ($this->addLineNumbers) { + \fwrite($output, '@@ -' . $fromStart); + + if (!$this->collapseRanges || 1 !== $fromRange) { + \fwrite($output, ',' . $fromRange); + } + + \fwrite($output, ' +' . $toStart); + + if (!$this->collapseRanges || 1 !== $toRange) { + \fwrite($output, ',' . $toRange); + } + + \fwrite($output, " @@\n"); + } else { + \fwrite($output, "@@ @@\n"); + } + + for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { + if ($diff[$i][1] === Differ::ADDED) { + \fwrite($output, '+' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::REMOVED) { + \fwrite($output, '-' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::OLD) { + \fwrite($output, ' ' . $diff[$i][0]); + } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { + \fwrite($output, "\n"); // $diff[$i][0] + } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ + \fwrite($output, ' ' . $diff[$i][0]); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\Output; + +use SebastianBergmann\Diff\Differ; + +/** + * Builds a diff string representation in a loose unified diff format + * listing only changes lines. Does not include line numbers. + */ +final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface +{ + /** + * @var string + */ + private $header; + + public function __construct(string $header = "--- Original\n+++ New\n") + { + $this->header = $header; + } + + public function getDiff(array $diff): string + { + $buffer = \fopen('php://memory', 'r+b'); + + if ('' !== $this->header) { + \fwrite($buffer, $this->header); + + if ("\n" !== \substr($this->header, -1, 1)) { + \fwrite($buffer, "\n"); + } + } + + foreach ($diff as $diffEntry) { + if ($diffEntry[1] === Differ::ADDED) { + \fwrite($buffer, '+' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::REMOVED) { + \fwrite($buffer, '-' . $diffEntry[0]); + } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { + \fwrite($buffer, ' ' . $diffEntry[0]); + + continue; // Warnings should not be tested for line break, it will always be there + } else { /* Not changed (old) 0 */ + continue; // we didn't write the non changs line, so do not add a line break either + } + + $lc = \substr($diffEntry[0], -1); + + if ($lc !== "\n" && $lc !== "\r") { + \fwrite($buffer, "\n"); // \No newline at end of file + } + } + + $diff = \stream_get_contents($buffer, -1, 0); + \fclose($buffer); + + return $diff; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +final class Diff +{ + /** + * @var string + */ + private $from; + + /** + * @var string + */ + private $to; + + /** + * @var Chunk[] + */ + private $chunks; + + /** + * @param string $from + * @param string $to + * @param Chunk[] $chunks + */ + public function __construct(string $from, string $to, array $chunks = []) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + + public function getFrom(): string + { + return $this->from; + } + + public function getTo(): string + { + return $this->to; + } + + /** + * @return Chunk[] + */ + public function getChunks(): array + { + return $this->chunks; + } + + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks): void + { + $this->chunks = $chunks; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * Unified diff parser. + */ +final class Parser +{ + /** + * @param string $string + * + * @return Diff[] + */ + public function parse(string $string): array + { + $lines = \preg_split('(\r\n|\r|\n)', $string); + + if (!empty($lines) && $lines[\count($lines) - 1] === '') { + \array_pop($lines); + } + + $lineCount = \count($lines); + $diffs = []; + $diff = null; + $collected = []; + + for ($i = 0; $i < $lineCount; ++$i) { + if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && + \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + + $diffs[] = $diff; + $collected = []; + } + + $diff = new Diff($fromMatch['file'], $toMatch['file']); + + ++$i; + } else { + if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; + } + + $collected[] = $lines[$i]; + } + } + + if ($diff !== null && \count($collected)) { + $this->parseFileDiff($diff, $collected); + + $diffs[] = $diff; + } + + return $diffs; + } + + private function parseFileDiff(Diff $diff, array $lines): void + { + $chunks = []; + $chunk = null; + + foreach ($lines as $line) { + if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { + $chunk = new Chunk( + (int) $match['start'], + isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, + (int) $match['end'], + isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 + ); + + $chunks[] = $chunk; + $diffLines = []; + + continue; + } + + if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + + if ($match['type'] === '+') { + $type = Line::ADDED; + } elseif ($match['type'] === '-') { + $type = Line::REMOVED; + } + + $diffLines[] = new Line($type, $match['line']); + + if (null !== $chunk) { + $chunk->setLines($diffLines); + } + } + } + + $diff->setChunks($chunks); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +final class Chunk +{ + /** + * @var int + */ + private $start; + + /** + * @var int + */ + private $startRange; + + /** + * @var int + */ + private $end; + + /** + * @var int + */ + private $endRange; + + /** + * @var Line[] + */ + private $lines; + + public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) + { + $this->start = $start; + $this->startRange = $startRange; + $this->end = $end; + $this->endRange = $endRange; + $this->lines = $lines; + } + + public function getStart(): int + { + return $this->start; + } + + public function getStartRange(): int + { + return $this->startRange; + } + + public function getEnd(): int + { + return $this->end; + } + + public function getEndRange(): int + { + return $this->endRange; + } + + /** + * @return Line[] + */ + public function getLines(): array + { + return $this->lines; + } + + /** + * @param Line[] $lines + */ + public function setLines(array $lines): void + { + foreach ($lines as $line) { + if (!$line instanceof Line) { + throw new InvalidArgumentException; + } + } + + $this->lines = $lines; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +interface LongestCommonSubsequenceCalculator +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * + * @return array + */ + public function calculate(array $from, array $to): array; +} +sebastian/diff + +Copyright (c) 2002-2019, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Exception that is raised when @covers must be used but is not. + */ +final class MissingCoversAnnotationException extends RuntimeException +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +class RuntimeException extends \RuntimeException implements Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Exception interface for php-code-coverage component. + */ +interface Exception +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Exception that is raised when covered code is not executed. + */ +final class CoveredCodeNotExecutedException extends RuntimeException +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +final class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ + /** + * @param int $argument + * @param string $type + * @param null|mixed $value + * + * @return InvalidArgumentException + */ + public static function create($argument, $type, $value = null): self + { + $stack = \debug_backtrace(0); + + return new self( + \sprintf( + 'Argument #%d%sof %s::%s() must be a %s', + $argument, + $value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ', + $stack[1]['class'], + $stack[1]['function'], + $type + ) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Exception that is raised when code is unintentionally covered. + */ +final class UnintentionallyCoveredCodeException extends RuntimeException +{ + /** + * @var array + */ + private $unintentionallyCoveredUnits = []; + + public function __construct(array $unintentionallyCoveredUnits) + { + $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits; + + parent::__construct($this->toString()); + } + + public function getUnintentionallyCoveredUnits(): array + { + return $this->unintentionallyCoveredUnits; + } + + private function toString(): string + { + $message = ''; + + foreach ($this->unintentionallyCoveredUnits as $unit) { + $message .= '- ' . $unit . "\n"; + } + + return $message; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\RuntimeException; + +/** + * Generates a Clover XML logfile from a code coverage object. + */ +final class Clover +{ + /** + * @throws \RuntimeException + */ + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string + { + $xmlDocument = new \DOMDocument('1.0', 'UTF-8'); + $xmlDocument->formatOutput = true; + + $xmlCoverage = $xmlDocument->createElement('coverage'); + $xmlCoverage->setAttribute('generated', (int) $_SERVER['REQUEST_TIME']); + $xmlDocument->appendChild($xmlCoverage); + + $xmlProject = $xmlDocument->createElement('project'); + $xmlProject->setAttribute('timestamp', (int) $_SERVER['REQUEST_TIME']); + + if (\is_string($name)) { + $xmlProject->setAttribute('name', $name); + } + + $xmlCoverage->appendChild($xmlProject); + + $packages = []; + $report = $coverage->getReport(); + + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + + /* @var File $item */ + + $xmlFile = $xmlDocument->createElement('file'); + $xmlFile->setAttribute('name', $item->getPath()); + + $classes = $item->getClassesAndTraits(); + $coverageData = $item->getCoverageData(); + $lines = []; + $namespace = 'global'; + + foreach ($classes as $className => $class) { + $classStatements = 0; + $coveredClassStatements = 0; + $coveredMethods = 0; + $classMethods = 0; + + foreach ($class['methods'] as $methodName => $method) { + if ($method['executableLines'] == 0) { + continue; + } + + $classMethods++; + $classStatements += $method['executableLines']; + $coveredClassStatements += $method['executedLines']; + + if ($method['coverage'] == 100) { + $coveredMethods++; + } + + $methodCount = 0; + + foreach (\range($method['startLine'], $method['endLine']) as $line) { + if (isset($coverageData[$line]) && ($coverageData[$line] !== null)) { + $methodCount = \max($methodCount, \count($coverageData[$line])); + } + } + + $lines[$method['startLine']] = [ + 'ccn' => $method['ccn'], + 'count' => $methodCount, + 'crap' => $method['crap'], + 'type' => 'method', + 'visibility' => $method['visibility'], + 'name' => $methodName, + ]; + } + + if (!empty($class['package']['namespace'])) { + $namespace = $class['package']['namespace']; + } + + $xmlClass = $xmlDocument->createElement('class'); + $xmlClass->setAttribute('name', $className); + $xmlClass->setAttribute('namespace', $namespace); + + if (!empty($class['package']['fullPackage'])) { + $xmlClass->setAttribute( + 'fullPackage', + $class['package']['fullPackage'] + ); + } + + if (!empty($class['package']['category'])) { + $xmlClass->setAttribute( + 'category', + $class['package']['category'] + ); + } + + if (!empty($class['package']['package'])) { + $xmlClass->setAttribute( + 'package', + $class['package']['package'] + ); + } + + if (!empty($class['package']['subpackage'])) { + $xmlClass->setAttribute( + 'subpackage', + $class['package']['subpackage'] + ); + } + + $xmlFile->appendChild($xmlClass); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('complexity', $class['ccn']); + $xmlMetrics->setAttribute('methods', $classMethods); + $xmlMetrics->setAttribute('coveredmethods', $coveredMethods); + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute('statements', $classStatements); + $xmlMetrics->setAttribute('coveredstatements', $coveredClassStatements); + $xmlMetrics->setAttribute('elements', $classMethods + $classStatements /* + conditionals */); + $xmlMetrics->setAttribute('coveredelements', $coveredMethods + $coveredClassStatements /* + coveredconditionals */); + $xmlClass->appendChild($xmlMetrics); + } + + foreach ($coverageData as $line => $data) { + if ($data === null || isset($lines[$line])) { + continue; + } + + $lines[$line] = [ + 'count' => \count($data), 'type' => 'stmt', + ]; + } + + \ksort($lines); + + foreach ($lines as $line => $data) { + $xmlLine = $xmlDocument->createElement('line'); + $xmlLine->setAttribute('num', $line); + $xmlLine->setAttribute('type', $data['type']); + + if (isset($data['name'])) { + $xmlLine->setAttribute('name', $data['name']); + } + + if (isset($data['visibility'])) { + $xmlLine->setAttribute('visibility', $data['visibility']); + } + + if (isset($data['ccn'])) { + $xmlLine->setAttribute('complexity', $data['ccn']); + } + + if (isset($data['crap'])) { + $xmlLine->setAttribute('crap', $data['crap']); + } + + $xmlLine->setAttribute('count', $data['count']); + $xmlFile->appendChild($xmlLine); + } + + $linesOfCode = $item->getLinesOfCode(); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); + $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); + $xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits()); + $xmlMetrics->setAttribute('methods', $item->getNumMethods()); + $xmlMetrics->setAttribute('coveredmethods', $item->getNumTestedMethods()); + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute('statements', $item->getNumExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', $item->getNumExecutedLines()); + $xmlMetrics->setAttribute('elements', $item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */); + $xmlMetrics->setAttribute('coveredelements', $item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */); + $xmlFile->appendChild($xmlMetrics); + + if ($namespace === 'global') { + $xmlProject->appendChild($xmlFile); + } else { + if (!isset($packages[$namespace])) { + $packages[$namespace] = $xmlDocument->createElement( + 'package' + ); + + $packages[$namespace]->setAttribute('name', $namespace); + $xmlProject->appendChild($packages[$namespace]); + } + + $packages[$namespace]->appendChild($xmlFile); + } + } + + $linesOfCode = $report->getLinesOfCode(); + + $xmlMetrics = $xmlDocument->createElement('metrics'); + $xmlMetrics->setAttribute('files', \count($report)); + $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); + $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); + $xmlMetrics->setAttribute('classes', $report->getNumClassesAndTraits()); + $xmlMetrics->setAttribute('methods', $report->getNumMethods()); + $xmlMetrics->setAttribute('coveredmethods', $report->getNumTestedMethods()); + $xmlMetrics->setAttribute('conditionals', 0); + $xmlMetrics->setAttribute('coveredconditionals', 0); + $xmlMetrics->setAttribute('statements', $report->getNumExecutableLines()); + $xmlMetrics->setAttribute('coveredstatements', $report->getNumExecutedLines()); + $xmlMetrics->setAttribute('elements', $report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */); + $xmlMetrics->setAttribute('coveredelements', $report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */); + $xmlProject->appendChild($xmlMetrics); + + $buffer = $xmlDocument->saveXML(); + + if ($target !== null) { + if (!$this->createDirectory(\dirname($target))) { + throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); + } + + if (@\file_put_contents($target, $buffer) === false) { + throw new RuntimeException( + \sprintf( + 'Could not write to "%s', + $target + ) + ); + } + } + + return $buffer; + } + + private function createDirectory(string $directory): bool + { + return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\RuntimeException; + +/** + * Uses var_export() to write a SebastianBergmann\CodeCoverage\CodeCoverage object to a file. + */ +final class PHP +{ + /** + * @throws \SebastianBergmann\CodeCoverage\RuntimeException + */ + public function process(CodeCoverage $coverage, ?string $target = null): string + { + $filter = $coverage->filter(); + + $buffer = \sprintf( + 'setData(%s); +$coverage->setTests(%s); + +$filter = $coverage->filter(); +$filter->setWhitelistedFiles(%s); + +return $coverage;', + \var_export($coverage->getData(true), 1), + \var_export($coverage->getTests(), 1), + \var_export($filter->getWhitelistedFiles(), 1) + ); + + if ($target !== null) { + if (!$this->createDirectory(\dirname($target))) { + throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); + } + + if (@\file_put_contents($target, $buffer) === false) { + throw new RuntimeException( + \sprintf( + 'Could not write to "%s', + $target + ) + ); + } + } + + return $buffer; + } + + private function createDirectory(string $directory): bool + { + return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +final class Tests +{ + private $contextNode; + + private $codeMap = [ + -1 => 'UNKNOWN', // PHPUnit_Runner_BaseTestRunner::STATUS_UNKNOWN + 0 => 'PASSED', // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED + 1 => 'SKIPPED', // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED + 2 => 'INCOMPLETE', // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE + 3 => 'FAILURE', // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE + 4 => 'ERROR', // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR + 5 => 'RISKY', // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY + 6 => 'WARNING', // PHPUnit_Runner_BaseTestRunner::STATUS_WARNING + ]; + + public function __construct(\DOMElement $context) + { + $this->contextNode = $context; + } + + public function addTest(string $test, array $result): void + { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'test' + ) + ); + + $node->setAttribute('name', $test); + $node->setAttribute('size', $result['size']); + $node->setAttribute('result', (int) $result['status']); + $node->setAttribute('status', $this->codeMap[(int) $result['status']]); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +final class Directory extends Node +{ +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use TheSeer\Tokenizer\NamespaceUri; +use TheSeer\Tokenizer\Tokenizer; +use TheSeer\Tokenizer\XMLSerializer; + +final class Source +{ + /** @var \DOMElement */ + private $context; + + public function __construct(\DOMElement $context) + { + $this->context = $context; + } + + public function setSourceCode(string $source): void + { + $context = $this->context; + + $tokens = (new Tokenizer())->parse($source); + $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens); + + $context->parentNode->replaceChild( + $context->ownerDocument->importNode($srcDom->documentElement, true), + $context + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use SebastianBergmann\CodeCoverage\Util; + +final class Totals +{ + /** + * @var \DOMNode + */ + private $container; + + /** + * @var \DOMElement + */ + private $linesNode; + + /** + * @var \DOMElement + */ + private $methodsNode; + + /** + * @var \DOMElement + */ + private $functionsNode; + + /** + * @var \DOMElement + */ + private $classesNode; + + /** + * @var \DOMElement + */ + private $traitsNode; + + public function __construct(\DOMElement $container) + { + $this->container = $container; + $dom = $container->ownerDocument; + + $this->linesNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'lines' + ); + + $this->methodsNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'methods' + ); + + $this->functionsNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'functions' + ); + + $this->classesNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'classes' + ); + + $this->traitsNode = $dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'traits' + ); + + $container->appendChild($this->linesNode); + $container->appendChild($this->methodsNode); + $container->appendChild($this->functionsNode); + $container->appendChild($this->classesNode); + $container->appendChild($this->traitsNode); + } + + public function getContainer(): \DOMNode + { + return $this->container; + } + + public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void + { + $this->linesNode->setAttribute('total', $loc); + $this->linesNode->setAttribute('comments', $cloc); + $this->linesNode->setAttribute('code', $ncloc); + $this->linesNode->setAttribute('executable', $executable); + $this->linesNode->setAttribute('executed', $executed); + $this->linesNode->setAttribute( + 'percent', + $executable === 0 ? 0 : \sprintf('%01.2F', Util::percent($executed, $executable)) + ); + } + + public function setNumClasses(int $count, int $tested): void + { + $this->classesNode->setAttribute('count', $count); + $this->classesNode->setAttribute('tested', $tested); + $this->classesNode->setAttribute( + 'percent', + $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) + ); + } + + public function setNumTraits(int $count, int $tested): void + { + $this->traitsNode->setAttribute('count', $count); + $this->traitsNode->setAttribute('tested', $tested); + $this->traitsNode->setAttribute( + 'percent', + $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) + ); + } + + public function setNumMethods(int $count, int $tested): void + { + $this->methodsNode->setAttribute('count', $count); + $this->methodsNode->setAttribute('tested', $tested); + $this->methodsNode->setAttribute( + 'percent', + $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) + ); + } + + public function setNumFunctions(int $count, int $tested): void + { + $this->functionsNode->setAttribute('count', $count); + $this->functionsNode->setAttribute('tested', $tested); + $this->functionsNode->setAttribute( + 'percent', + $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use SebastianBergmann\CodeCoverage\RuntimeException; + +final class Coverage +{ + /** + * @var \XMLWriter + */ + private $writer; + + /** + * @var \DOMElement + */ + private $contextNode; + + /** + * @var bool + */ + private $finalized = false; + + public function __construct(\DOMElement $context, string $line) + { + $this->contextNode = $context; + + $this->writer = new \XMLWriter(); + $this->writer->openMemory(); + $this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); + $this->writer->writeAttribute('nr', $line); + } + + /** + * @throws RuntimeException + */ + public function addTest(string $test): void + { + if ($this->finalized) { + throw new RuntimeException('Coverage Report already finalized'); + } + + $this->writer->startElement('covered'); + $this->writer->writeAttribute('by', $test); + $this->writer->endElement(); + } + + public function finalize(): void + { + $this->writer->endElement(); + + $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); + $fragment->appendXML($this->writer->outputMemory()); + + $this->contextNode->parentNode->replaceChild( + $fragment, + $this->contextNode + ); + + $this->finalized = true; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +final class Report extends File +{ + public function __construct(string $name) + { + $dom = new \DOMDocument(); + $dom->loadXML(''); + + $contextNode = $dom->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'file' + )->item(0); + + parent::__construct($contextNode); + + $this->setName($name); + } + + public function asDom(): \DOMDocument + { + return $this->getDomDocument(); + } + + public function getFunctionObject($name): Method + { + $node = $this->getContextNode()->appendChild( + $this->getDomDocument()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'function' + ) + ); + + return new Method($node, $name); + } + + public function getClassObject($name): Unit + { + return $this->getUnitObject('class', $name); + } + + public function getTraitObject($name): Unit + { + return $this->getUnitObject('trait', $name); + } + + public function getSource(): Source + { + $source = $this->getContextNode()->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'source' + )->item(0); + + if (!$source) { + $source = $this->getContextNode()->appendChild( + $this->getDomDocument()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'source' + ) + ); + } + + return new Source($source); + } + + private function setName($name): void + { + $this->getContextNode()->setAttribute('name', \basename($name)); + $this->getContextNode()->setAttribute('path', \dirname($name)); + } + + private function getUnitObject($tagName, $name): Unit + { + $node = $this->getContextNode()->appendChild( + $this->getDomDocument()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + $tagName + ) + ); + + return new Unit($node, $name); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +final class Method +{ + /** + * @var \DOMElement + */ + private $contextNode; + + public function __construct(\DOMElement $context, string $name) + { + $this->contextNode = $context; + + $this->setName($name); + } + + public function setSignature(string $signature): void + { + $this->contextNode->setAttribute('signature', $signature); + } + + public function setLines(string $start, ?string $end = null): void + { + $this->contextNode->setAttribute('start', $start); + + if ($end !== null) { + $this->contextNode->setAttribute('end', $end); + } + } + + public function setTotals(string $executable, string $executed, string $coverage): void + { + $this->contextNode->setAttribute('executable', $executable); + $this->contextNode->setAttribute('executed', $executed); + $this->contextNode->setAttribute('coverage', $coverage); + } + + public function setCrap(string $crap): void + { + $this->contextNode->setAttribute('crap', $crap); + } + + private function setName(string $name): void + { + $this->contextNode->setAttribute('name', $name); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\AbstractNode; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\CodeCoverage\Node\File as FileNode; +use SebastianBergmann\CodeCoverage\RuntimeException; +use SebastianBergmann\CodeCoverage\Version; +use SebastianBergmann\Environment\Runtime; + +final class Facade +{ + /** + * @var string + */ + private $target; + + /** + * @var Project + */ + private $project; + + /** + * @var string + */ + private $phpUnitVersion; + + public function __construct(string $version) + { + $this->phpUnitVersion = $version; + } + + /** + * @throws RuntimeException + */ + public function process(CodeCoverage $coverage, string $target): void + { + if (\substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) { + $target .= \DIRECTORY_SEPARATOR; + } + + $this->target = $target; + $this->initTargetDirectory($target); + + $report = $coverage->getReport(); + + $this->project = new Project( + $coverage->getReport()->getName() + ); + + $this->setBuildInformation(); + $this->processTests($coverage->getTests()); + $this->processDirectory($report, $this->project); + + $this->saveDocument($this->project->asDom(), 'index'); + } + + private function setBuildInformation(): void + { + $buildNode = $this->project->getBuildInformation(); + $buildNode->setRuntimeInformation(new Runtime()); + $buildNode->setBuildTime(\DateTime::createFromFormat('U', $_SERVER['REQUEST_TIME'])); + $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); + } + + /** + * @throws RuntimeException + */ + private function initTargetDirectory(string $directory): void + { + if (\file_exists($directory)) { + if (!\is_dir($directory)) { + throw new RuntimeException( + "'$directory' exists but is not a directory." + ); + } + + if (!\is_writable($directory)) { + throw new RuntimeException( + "'$directory' exists but is not writable." + ); + } + } elseif (!$this->createDirectory($directory)) { + throw new RuntimeException( + "'$directory' could not be created." + ); + } + } + + private function processDirectory(DirectoryNode $directory, Node $context): void + { + $directoryName = $directory->getName(); + + if ($this->project->getProjectSourceDirectory() === $directoryName) { + $directoryName = '/'; + } + + $directoryObject = $context->addDirectory($directoryName); + + $this->setTotals($directory, $directoryObject->getTotals()); + + foreach ($directory->getDirectories() as $node) { + $this->processDirectory($node, $directoryObject); + } + + foreach ($directory->getFiles() as $node) { + $this->processFile($node, $directoryObject); + } + } + + /** + * @throws RuntimeException + */ + private function processFile(FileNode $file, Directory $context): void + { + $fileObject = $context->addFile( + $file->getName(), + $file->getId() . '.xml' + ); + + $this->setTotals($file, $fileObject->getTotals()); + + $path = \substr( + $file->getPath(), + \strlen($this->project->getProjectSourceDirectory()) + ); + + $fileReport = new Report($path); + + $this->setTotals($file, $fileReport->getTotals()); + + foreach ($file->getClassesAndTraits() as $unit) { + $this->processUnit($unit, $fileReport); + } + + foreach ($file->getFunctions() as $function) { + $this->processFunction($function, $fileReport); + } + + foreach ($file->getCoverageData() as $line => $tests) { + if (!\is_array($tests) || \count($tests) === 0) { + continue; + } + + $coverage = $fileReport->getLineCoverage($line); + + foreach ($tests as $test) { + $coverage->addTest($test); + } + + $coverage->finalize(); + } + + $fileReport->getSource()->setSourceCode( + \file_get_contents($file->getPath()) + ); + + $this->saveDocument($fileReport->asDom(), $file->getId()); + } + + private function processUnit(array $unit, Report $report): void + { + if (isset($unit['className'])) { + $unitObject = $report->getClassObject($unit['className']); + } else { + $unitObject = $report->getTraitObject($unit['traitName']); + } + + $unitObject->setLines( + $unit['startLine'], + $unit['executableLines'], + $unit['executedLines'] + ); + + $unitObject->setCrap($unit['crap']); + + $unitObject->setPackage( + $unit['package']['fullPackage'], + $unit['package']['package'], + $unit['package']['subpackage'], + $unit['package']['category'] + ); + + $unitObject->setNamespace($unit['package']['namespace']); + + foreach ($unit['methods'] as $method) { + $methodObject = $unitObject->addMethod($method['methodName']); + $methodObject->setSignature($method['signature']); + $methodObject->setLines($method['startLine'], $method['endLine']); + $methodObject->setCrap($method['crap']); + $methodObject->setTotals( + $method['executableLines'], + $method['executedLines'], + $method['coverage'] + ); + } + } + + private function processFunction(array $function, Report $report): void + { + $functionObject = $report->getFunctionObject($function['functionName']); + + $functionObject->setSignature($function['signature']); + $functionObject->setLines($function['startLine']); + $functionObject->setCrap($function['crap']); + $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']); + } + + private function processTests(array $tests): void + { + $testsObject = $this->project->getTests(); + + foreach ($tests as $test => $result) { + if ($test === 'UNCOVERED_FILES_FROM_WHITELIST') { + continue; + } + + $testsObject->addTest($test, $result); + } + } + + private function setTotals(AbstractNode $node, Totals $totals): void + { + $loc = $node->getLinesOfCode(); + + $totals->setNumLines( + $loc['loc'], + $loc['cloc'], + $loc['ncloc'], + $node->getNumExecutableLines(), + $node->getNumExecutedLines() + ); + + $totals->setNumClasses( + $node->getNumClasses(), + $node->getNumTestedClasses() + ); + + $totals->setNumTraits( + $node->getNumTraits(), + $node->getNumTestedTraits() + ); + + $totals->setNumMethods( + $node->getNumMethods(), + $node->getNumTestedMethods() + ); + + $totals->setNumFunctions( + $node->getNumFunctions(), + $node->getNumTestedFunctions() + ); + } + + private function getTargetDirectory(): string + { + return $this->target; + } + + /** + * @throws RuntimeException + */ + private function saveDocument(\DOMDocument $document, string $name): void + { + $filename = \sprintf('%s/%s.xml', $this->getTargetDirectory(), $name); + + $document->formatOutput = true; + $document->preserveWhiteSpace = false; + $this->initTargetDirectory(\dirname($filename)); + + $document->save($filename); + } + + private function createDirectory(string $directory): bool + { + return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +final class Unit +{ + /** + * @var \DOMElement + */ + private $contextNode; + + public function __construct(\DOMElement $context, string $name) + { + $this->contextNode = $context; + + $this->setName($name); + } + + public function setLines(int $start, int $executable, int $executed): void + { + $this->contextNode->setAttribute('start', $start); + $this->contextNode->setAttribute('executable', $executable); + $this->contextNode->setAttribute('executed', $executed); + } + + public function setCrap(float $crap): void + { + $this->contextNode->setAttribute('crap', $crap); + } + + public function setPackage(string $full, string $package, string $sub, string $category): void + { + $node = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'package' + )->item(0); + + if (!$node) { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'package' + ) + ); + } + + $node->setAttribute('full', $full); + $node->setAttribute('name', $package); + $node->setAttribute('sub', $sub); + $node->setAttribute('category', $category); + } + + public function setNamespace(string $namespace): void + { + $node = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'namespace' + )->item(0); + + if (!$node) { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'namespace' + ) + ); + } + + $node->setAttribute('name', $namespace); + } + + public function addMethod(string $name): Method + { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'method' + ) + ); + + return new Method($node, $name); + } + + private function setName(string $name): void + { + $this->contextNode->setAttribute('name', $name); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +use SebastianBergmann\Environment\Runtime; + +final class BuildInformation +{ + /** + * @var \DOMElement + */ + private $contextNode; + + public function __construct(\DOMElement $contextNode) + { + $this->contextNode = $contextNode; + } + + public function setRuntimeInformation(Runtime $runtime): void + { + $runtimeNode = $this->getNodeByName('runtime'); + + $runtimeNode->setAttribute('name', $runtime->getName()); + $runtimeNode->setAttribute('version', $runtime->getVersion()); + $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); + + $driverNode = $this->getNodeByName('driver'); + + if ($runtime->hasPHPDBGCodeCoverage()) { + $driverNode->setAttribute('name', 'phpdbg'); + $driverNode->setAttribute('version', \constant('PHPDBG_VERSION')); + } + + if ($runtime->hasXdebug()) { + $driverNode->setAttribute('name', 'xdebug'); + $driverNode->setAttribute('version', \phpversion('xdebug')); + } + } + + public function setBuildTime(\DateTime $date): void + { + $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); + } + + public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void + { + $this->contextNode->setAttribute('phpunit', $phpUnitVersion); + $this->contextNode->setAttribute('coverage', $coverageVersion); + } + + private function getNodeByName(string $name): \DOMElement + { + $node = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + $name + )->item(0); + + if (!$node) { + $node = $this->contextNode->appendChild( + $this->contextNode->ownerDocument->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + $name + ) + ); + } + + return $node; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +class File +{ + /** + * @var \DOMDocument + */ + private $dom; + + /** + * @var \DOMElement + */ + private $contextNode; + + public function __construct(\DOMElement $context) + { + $this->dom = $context->ownerDocument; + $this->contextNode = $context; + } + + public function getTotals(): Totals + { + $totalsContainer = $this->contextNode->firstChild; + + if (!$totalsContainer) { + $totalsContainer = $this->contextNode->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'totals' + ) + ); + } + + return new Totals($totalsContainer); + } + + public function getLineCoverage(string $line): Coverage + { + $coverage = $this->contextNode->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'coverage' + )->item(0); + + if (!$coverage) { + $coverage = $this->contextNode->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'coverage' + ) + ); + } + + $lineNode = $coverage->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'line' + ) + ); + + return new Coverage($lineNode, $line); + } + + protected function getContextNode(): \DOMElement + { + return $this->contextNode; + } + + protected function getDomDocument(): \DOMDocument + { + return $this->dom; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +abstract class Node +{ + /** + * @var \DOMDocument + */ + private $dom; + + /** + * @var \DOMElement + */ + private $contextNode; + + public function __construct(\DOMElement $context) + { + $this->setContextNode($context); + } + + public function getDom(): \DOMDocument + { + return $this->dom; + } + + public function getTotals(): Totals + { + $totalsContainer = $this->getContextNode()->firstChild; + + if (!$totalsContainer) { + $totalsContainer = $this->getContextNode()->appendChild( + $this->dom->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'totals' + ) + ); + } + + return new Totals($totalsContainer); + } + + public function addDirectory(string $name): Directory + { + $dirNode = $this->getDom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'directory' + ); + + $dirNode->setAttribute('name', $name); + $this->getContextNode()->appendChild($dirNode); + + return new Directory($dirNode); + } + + public function addFile(string $name, string $href): File + { + $fileNode = $this->getDom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'file' + ); + + $fileNode->setAttribute('name', $name); + $fileNode->setAttribute('href', $href); + $this->getContextNode()->appendChild($fileNode); + + return new File($fileNode); + } + + protected function setContextNode(\DOMElement $context): void + { + $this->dom = $context->ownerDocument; + $this->contextNode = $context; + } + + protected function getContextNode(): \DOMElement + { + return $this->contextNode; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Xml; + +final class Project extends Node +{ + public function __construct(string $directory) + { + $this->init(); + $this->setProjectSourceDirectory($directory); + } + + public function getProjectSourceDirectory(): string + { + return $this->getContextNode()->getAttribute('source'); + } + + public function getBuildInformation(): BuildInformation + { + $buildNode = $this->getDom()->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'build' + )->item(0); + + if (!$buildNode) { + $buildNode = $this->getDom()->documentElement->appendChild( + $this->getDom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'build' + ) + ); + } + + return new BuildInformation($buildNode); + } + + public function getTests(): Tests + { + $testsNode = $this->getContextNode()->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'tests' + )->item(0); + + if (!$testsNode) { + $testsNode = $this->getContextNode()->appendChild( + $this->getDom()->createElementNS( + 'https://schema.phpunit.de/coverage/1.0', + 'tests' + ) + ); + } + + return new Tests($testsNode); + } + + public function asDom(): \DOMDocument + { + return $this->getDom(); + } + + private function init(): void + { + $dom = new \DOMDocument; + $dom->loadXML(''); + + $this->setContextNode( + $dom->getElementsByTagNameNS( + 'https://schema.phpunit.de/coverage/1.0', + 'project' + )->item(0) + ); + } + + private function setProjectSourceDirectory(string $name): void + { + $this->getContextNode()->setAttribute('source', $name); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; + +/** + * Renders a directory node. + */ +final class Directory extends Renderer +{ + /** + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + public function render(DirectoryNode $node, string $file): void + { + $template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}'); + + $this->setCommonTemplateVariables($template, $node); + + $items = $this->renderItem($node, true); + + foreach ($node->getDirectories() as $item) { + $items .= $this->renderItem($item); + } + + foreach ($node->getFiles() as $item) { + $items .= $this->renderItem($item); + } + + $template->setVar( + [ + 'id' => $node->getId(), + 'items' => $items, + ] + ); + + $template->renderTo($file); + } + + protected function renderItem(Node $node, bool $total = false): string + { + $data = [ + 'numClasses' => $node->getNumClassesAndTraits(), + 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), + 'numMethods' => $node->getNumFunctionsAndMethods(), + 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), + 'linesExecutedPercent' => $node->getLineExecutedPercent(false), + 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), + 'numExecutedLines' => $node->getNumExecutedLines(), + 'numExecutableLines' => $node->getNumExecutableLines(), + 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), + 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), + 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), + 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), + ]; + + if ($total) { + $data['name'] = 'Total'; + } else { + if ($node instanceof DirectoryNode) { + $data['name'] = \sprintf( + '%s', + $node->getName(), + $node->getName() + ); + + $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); + + $data['icon'] = \sprintf('', $up); + } else { + $data['name'] = \sprintf( + '%s', + $node->getName(), + $node->getName() + ); + + $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); + + $data['icon'] = \sprintf('', $up); + } + } + + return $this->renderItemTemplate( + new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'), + $data + ); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use SebastianBergmann\CodeCoverage\Node\AbstractNode; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; + +/** + * Renders the dashboard for a directory node. + */ +final class Dashboard extends Renderer +{ + /** + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + public function render(DirectoryNode $node, string $file): void + { + $classes = $node->getClassesAndTraits(); + $template = new \Text_Template( + $this->templatePath . 'dashboard.html', + '{{', + '}}' + ); + + $this->setCommonTemplateVariables($template, $node); + + $baseLink = $node->getId() . '/'; + $complexity = $this->complexity($classes, $baseLink); + $coverageDistribution = $this->coverageDistribution($classes); + $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); + $projectRisks = $this->projectRisks($classes, $baseLink); + + $template->setVar( + [ + 'insufficient_coverage_classes' => $insufficientCoverage['class'], + 'insufficient_coverage_methods' => $insufficientCoverage['method'], + 'project_risks_classes' => $projectRisks['class'], + 'project_risks_methods' => $projectRisks['method'], + 'complexity_class' => $complexity['class'], + 'complexity_method' => $complexity['method'], + 'class_coverage_distribution' => $coverageDistribution['class'], + 'method_coverage_distribution' => $coverageDistribution['method'], + ] + ); + + $template->renderTo($file); + } + + /** + * Returns the data for the Class/Method Complexity charts. + */ + protected function complexity(array $classes, string $baseLink): array + { + $result = ['class' => [], 'method' => []]; + + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($className !== '*') { + $methodName = $className . '::' . $methodName; + } + + $result['method'][] = [ + $method['coverage'], + $method['ccn'], + \sprintf( + '%s', + \str_replace($baseLink, '', $method['link']), + $methodName + ), + ]; + } + + $result['class'][] = [ + $class['coverage'], + $class['ccn'], + \sprintf( + '%s', + \str_replace($baseLink, '', $class['link']), + $className + ), + ]; + } + + return [ + 'class' => \json_encode($result['class']), + 'method' => \json_encode($result['method']), + ]; + } + + /** + * Returns the data for the Class / Method Coverage Distribution chart. + */ + protected function coverageDistribution(array $classes): array + { + $result = [ + 'class' => [ + '0%' => 0, + '0-10%' => 0, + '10-20%' => 0, + '20-30%' => 0, + '30-40%' => 0, + '40-50%' => 0, + '50-60%' => 0, + '60-70%' => 0, + '70-80%' => 0, + '80-90%' => 0, + '90-100%' => 0, + '100%' => 0, + ], + 'method' => [ + '0%' => 0, + '0-10%' => 0, + '10-20%' => 0, + '20-30%' => 0, + '30-40%' => 0, + '40-50%' => 0, + '50-60%' => 0, + '60-70%' => 0, + '70-80%' => 0, + '80-90%' => 0, + '90-100%' => 0, + '100%' => 0, + ], + ]; + + foreach ($classes as $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] === 0) { + $result['method']['0%']++; + } elseif ($method['coverage'] === 100) { + $result['method']['100%']++; + } else { + $key = \floor($method['coverage'] / 10) * 10; + $key = $key . '-' . ($key + 10) . '%'; + $result['method'][$key]++; + } + } + + if ($class['coverage'] === 0) { + $result['class']['0%']++; + } elseif ($class['coverage'] === 100) { + $result['class']['100%']++; + } else { + $key = \floor($class['coverage'] / 10) * 10; + $key = $key . '-' . ($key + 10) . '%'; + $result['class'][$key]++; + } + } + + return [ + 'class' => \json_encode(\array_values($result['class'])), + 'method' => \json_encode(\array_values($result['method'])), + ]; + } + + /** + * Returns the classes / methods with insufficient coverage. + */ + protected function insufficientCoverage(array $classes, string $baseLink): array + { + $leastTestedClasses = []; + $leastTestedMethods = []; + $result = ['class' => '', 'method' => '']; + + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] < $this->highLowerBound) { + $key = $methodName; + + if ($className !== '*') { + $key = $className . '::' . $methodName; + } + + $leastTestedMethods[$key] = $method['coverage']; + } + } + + if ($class['coverage'] < $this->highLowerBound) { + $leastTestedClasses[$className] = $class['coverage']; + } + } + + \asort($leastTestedClasses); + \asort($leastTestedMethods); + + foreach ($leastTestedClasses as $className => $coverage) { + $result['class'] .= \sprintf( + ' %s%d%%' . "\n", + \str_replace($baseLink, '', $classes[$className]['link']), + $className, + $coverage + ); + } + + foreach ($leastTestedMethods as $methodName => $coverage) { + [$class, $method] = \explode('::', $methodName); + + $result['method'] .= \sprintf( + ' %s%d%%' . "\n", + \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), + $methodName, + $method, + $coverage + ); + } + + return $result; + } + + /** + * Returns the project risks according to the CRAP index. + */ + protected function projectRisks(array $classes, string $baseLink): array + { + $classRisks = []; + $methodRisks = []; + $result = ['class' => '', 'method' => '']; + + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) { + $key = $methodName; + + if ($className !== '*') { + $key = $className . '::' . $methodName; + } + + $methodRisks[$key] = $method['crap']; + } + } + + if ($class['coverage'] < $this->highLowerBound && + $class['ccn'] > \count($class['methods'])) { + $classRisks[$className] = $class['crap']; + } + } + + \arsort($classRisks); + \arsort($methodRisks); + + foreach ($classRisks as $className => $crap) { + $result['class'] .= \sprintf( + ' %s%d' . "\n", + \str_replace($baseLink, '', $classes[$className]['link']), + $className, + $crap + ); + } + + foreach ($methodRisks as $methodName => $crap) { + [$class, $method] = \explode('::', $methodName); + + $result['method'] .= \sprintf( + ' %s%d' . "\n", + \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), + $methodName, + $method, + $crap + ); + } + + return $result; + } + + protected function getActiveBreadcrumb(AbstractNode $node): string + { + return \sprintf( + ' ' . "\n" . + ' ' . "\n", + $node->getName() + ); + } +} +/* + Copyright (C) Federico Zivolo 2018 + Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). + */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=J(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right offset) { + $top_link.fadeIn(); + } else { + $top_link.fadeOut(); + } + }).scroll(); + + $('.popin') + .popover({trigger: 'manual'}) + .on({ + 'mouseenter.popover': function () { + var $target = $(this); + + $target.data('popover-hover', true); + + // popover already displayed + if ($target.next('.popover').length) { + return; + } + + // show the popover + $target.popover('show'); + + // register mouse events on the popover + $target.next('.popover:not(.popover-initialized)') + .on({ + 'mouseenter': function () { + $target.data('popover-hover', true); + }, + 'mouseleave': function () { + hidePopover($target); + } + }) + .addClass('popover-initialized'); + }, + 'mouseleave.popover': function () { + hidePopover($(this)); + } + }); + }); +/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */ +!function(){var a={};a.dev=!1,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.logs={},a.dom={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(a,b){console&&console.warn&&console.warn("nvd3 warning: `"+a+"` has been deprecated. ",b||"")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start();var c=function(){for(var d,e,f=0;b>f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); +x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); +var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
    open:"+b.yAxis.tickFormat()(c.open)+"
    close:"+b.yAxis.tickFormat()(c.close)+"
    high"+b.yAxis.tickFormat()(c.high)+"
    low:"+b.yAxis.tickFormat()(c.low)+"
    "}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
    open:"+b.yAxis.tickFormat()(c.open)+"
    close:"+b.yAxis.tickFormat()(c.close)+"
    high"+b.yAxis.tickFormat()(c.high)+"
    low:"+b.yAxis.tickFormat()(c.low)+"
    "}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] +}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale(); +var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left +}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b) +}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,h){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
    ',trigger:"hover focus",title:"",delay:0,html:!(Ie={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Se={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},we="out",Ne={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:(De="show")+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Oe="fade",ke="show",Pe=".tooltip-inner",je=".arrow",He="hover",Le="focus",Re="click",xe="manual",We=function(){function i(t,e){if("undefined"==typeof h)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=pe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(pe(this.getTipElement()).hasClass(ke))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),pe.removeData(this.element,this.constructor.DATA_KEY),pe(this.element).off(this.constructor.EVENT_KEY),pe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&pe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===pe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=pe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){pe(this.element).trigger(t);var n=pe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Fn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&pe(i).addClass(Oe);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:pe(document).find(this.config.container);pe(i).data(this.constructor.DATA_KEY,this),pe.contains(this.element.ownerDocument.documentElement,this.tip)||pe(i).appendTo(a),pe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new h(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),pe(i).addClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().on("mouseover",null,pe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,pe(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(pe(this.tip).hasClass(Oe)){var c=Fn.getTransitionDurationFromElement(this.tip);pe(this.tip).one(Fn.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=pe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),pe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(pe(this.element).trigger(i),!i.isDefaultPrevented()){if(pe(n).removeClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().off("mouseover",null,pe.noop),this._activeTrigger[Re]=!1,this._activeTrigger[Le]=!1,this._activeTrigger[He]=!1,pe(this.tip).hasClass(Oe)){var o=Fn.getTransitionDurationFromElement(n);pe(n).one(Fn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){pe(this.getTipElement()).addClass(Te+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||pe(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(pe(t.querySelectorAll(Pe)),this.getTitle()),pe(t).removeClass(Oe+" "+ke)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?pe(e).parent().is(t)||t.empty().append(e):t.text(pe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return Ie[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)pe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==xe){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;pe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}pe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Le:He]=!0),pe(e.getTipElement()).hasClass(ke)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Le:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,pe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Fn.typeCheckConfig(ve,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=pe(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(pe(t).removeClass(Oe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=pe(this).data(ye),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),pe(this).data(ye,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Ae}},{key:"NAME",get:function(){return ve}},{key:"DATA_KEY",get:function(){return ye}},{key:"Event",get:function(){return Ne}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}(),pe.fn[ve]=We._jQueryInterface,pe.fn[ve].Constructor=We,pe.fn[ve].noConflict=function(){return pe.fn[ve]=Ce,We._jQueryInterface},We),Jn=(qe="popover",Ke="."+(Fe="bs.popover"),Me=(Ue=e).fn[qe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},zn.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},zn.DefaultType,{content:"(string|element|function)"}),ze="fade",Ze=".popover-header",Ge=".popover-body",$e={HIDE:"hide"+Ke,HIDDEN:"hidden"+Ke,SHOW:(Je="show")+Ke,SHOWN:"shown"+Ke,INSERTED:"inserted"+Ke,CLICK:"click"+Ke,FOCUSIN:"focusin"+Ke,FOCUSOUT:"focusout"+Ke,MOUSEENTER:"mouseenter"+Ke,MOUSELEAVE:"mouseleave"+Ke},Xe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Ue(this.getTipElement()).addClass(Qe+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Ue(this.config.template)[0],this.tip},r.setContent=function(){var t=Ue(this.getTipElement());this.setElementContent(t.find(Ze),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ge),e),t.removeClass(ze+" "+Je)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Ue(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||tn?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ +r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], +shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; +if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + + + + +
    +
    + {{percent}}% covered ({{level}}) +
    +
    + + + + + Dashboard for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +

    Classes

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + +{{insufficient_coverage_classes}} + +
    ClassCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + +{{project_risks_classes}} + +
    ClassCRAP
    +
    +
    +
    +
    +
    +

    Methods

    +
    +
    +
    +
    +

    Coverage Distribution

    +
    + +
    +
    +
    +

    Complexity

    +
    + +
    +
    +
    +
    +
    +

    Insufficient Coverage

    +
    + + + + + + + + +{{insufficient_coverage_methods}} + +
    MethodCoverage
    +
    +
    +
    +

    Project Risks

    +
    + + + + + + + + +{{project_risks_methods}} + +
    MethodCRAP
    +
    +
    +
    + +
    + + + + + + + + + + + Code Coverage for {{full_path}} + + + + + + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + +{{items}} + +
     
    Code Coverage
     
    Lines
    Functions and Methods
    Classes and Traits
    +
    +
    +
    +

    Legend

    +

    + Low: 0% to {{low_upper_bound}}% + Medium: {{low_upper_bound}}% to {{high_lower_bound}}% + High: {{high_lower_bound}}% to 100% +

    +

    + Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. +

    +
    +
    + + + + {{name}} + {{methods_bar}} +
    {{methods_tested_percent}}
    +
    {{methods_number}}
    + {{crap}} + {{lines_bar}} +
    {{lines_executed_percent}}
    +
    {{lines_number}}
    + + +.octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; +} +/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label::after,.was-validated .custom-file-input:valid~.custom-file-label::after{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label::after,.was-validated .custom-file-input:invalid~.custom-file-label::after{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;background-color:transparent}.btn-link:hover{color:#0056b3;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link.focus,.btn-link:focus{text-decoration:underline;border-color:transparent;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label::before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-label::after{border-color:#80bdff}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.25rem;padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;padding-left:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{color:#000;text-decoration:none;opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-25%);transform:translate(0,-25%)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:translate(0,0);transform:translate(0,0)}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-ms-flex-align:center;align-items:center;width:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-item-next,.carousel-item-prev,.carousel-item.active{transition:none}}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-fade .carousel-item{opacity:0;transition-duration:.6s;transition-property:opacity}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{opacity:0}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:rgba(255,255,255,.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0062cc!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#545b62!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ +body { + padding-top: 10px; +} + +.popover { + max-width: none; +} + +.octicon { + margin-right:.25em; +} + +.table-bordered>thead>tr>td { + border-bottom-width: 1px; +} + +.table tbody>tr>td, .table thead>tr>td { + padding-top: 3px; + padding-bottom: 3px; +} + +.table-condensed tbody>tr>td { + padding-top: 0; + padding-bottom: 0; +} + +.table .progress { + margin-bottom: inherit; +} + +.table-borderless th, .table-borderless td { + border: 0 !important; +} + +.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { + background-color: #dff0d8; +} + +.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { + background-color: #c3e3b5; +} + +.table tbody tr.covered-by-small-tests, li.covered-by-small-tests { + background-color: #99cb84; +} + +.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { + background-color: #f2dede; +} + +.table tbody td.warning, li.warning, span.warning { + background-color: #fcf8e3; +} + +.table tbody td.info { + background-color: #d9edf7; +} + +td.big { + width: 117px; +} + +td.small { +} + +td.codeLine { + font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + white-space: pre; +} + +td span.comment { + color: #888a85; +} + +td span.default { + color: #2e3436; +} + +td span.html { + color: #888a85; +} + +td span.keyword { + color: #2e3436; + font-weight: bold; +} + +pre span.string { + color: #2e3436; +} + +span.success, span.warning, span.danger { + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +#classCoverageDistribution, #classComplexity { + height: 200px; + width: 475px; +} + +#toplink { + position: fixed; + left: 5px; + bottom: 5px; + outline: 0; +} + +svg text { + font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; + color: #666; + fill: #666; +} + +.scrollbox { + height:245px; + overflow-x:hidden; + overflow-y:scroll; +} +.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use SebastianBergmann\CodeCoverage\Node\File as FileNode; +use SebastianBergmann\CodeCoverage\Util; + +/** + * Renders a file node. + */ +final class File extends Renderer +{ + /** + * @var int + */ + private $htmlSpecialCharsFlags = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE; + + /** + * @throws \RuntimeException + */ + public function render(FileNode $node, string $file): void + { + $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}'); + + $template->setVar( + [ + 'items' => $this->renderItems($node), + 'lines' => $this->renderSource($node), + ] + ); + + $this->setCommonTemplateVariables($template, $node); + + $template->renderTo($file); + } + + protected function renderItems(FileNode $node): string + { + $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}'); + + $methodItemTemplate = new \Text_Template( + $this->templatePath . 'method_item.html', + '{{', + '}}' + ); + + $items = $this->renderItemTemplate( + $template, + [ + 'name' => 'Total', + 'numClasses' => $node->getNumClassesAndTraits(), + 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), + 'numMethods' => $node->getNumFunctionsAndMethods(), + 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), + 'linesExecutedPercent' => $node->getLineExecutedPercent(false), + 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), + 'numExecutedLines' => $node->getNumExecutedLines(), + 'numExecutableLines' => $node->getNumExecutableLines(), + 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), + 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), + 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), + 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), + 'crap' => 'CRAP', + ] + ); + + $items .= $this->renderFunctionItems( + $node->getFunctions(), + $methodItemTemplate + ); + + $items .= $this->renderTraitOrClassItems( + $node->getTraits(), + $template, + $methodItemTemplate + ); + + $items .= $this->renderTraitOrClassItems( + $node->getClasses(), + $template, + $methodItemTemplate + ); + + return $items; + } + + protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate): string + { + $buffer = ''; + + if (empty($items)) { + return $buffer; + } + + foreach ($items as $name => $item) { + $numMethods = 0; + $numTestedMethods = 0; + + foreach ($item['methods'] as $method) { + if ($method['executableLines'] > 0) { + $numMethods++; + + if ($method['executedLines'] === $method['executableLines']) { + $numTestedMethods++; + } + } + } + + if ($item['executableLines'] > 0) { + $numClasses = 1; + $numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0; + $linesExecutedPercentAsString = Util::percent( + $item['executedLines'], + $item['executableLines'], + true + ); + } else { + $numClasses = 'n/a'; + $numTestedClasses = 'n/a'; + $linesExecutedPercentAsString = 'n/a'; + } + + $buffer .= $this->renderItemTemplate( + $template, + [ + 'name' => $this->abbreviateClassName($name), + 'numClasses' => $numClasses, + 'numTestedClasses' => $numTestedClasses, + 'numMethods' => $numMethods, + 'numTestedMethods' => $numTestedMethods, + 'linesExecutedPercent' => Util::percent( + $item['executedLines'], + $item['executableLines'], + false + ), + 'linesExecutedPercentAsString' => $linesExecutedPercentAsString, + 'numExecutedLines' => $item['executedLines'], + 'numExecutableLines' => $item['executableLines'], + 'testedMethodsPercent' => Util::percent( + $numTestedMethods, + $numMethods + ), + 'testedMethodsPercentAsString' => Util::percent( + $numTestedMethods, + $numMethods, + true + ), + 'testedClassesPercent' => Util::percent( + $numTestedMethods == $numMethods ? 1 : 0, + 1 + ), + 'testedClassesPercentAsString' => Util::percent( + $numTestedMethods == $numMethods ? 1 : 0, + 1, + true + ), + 'crap' => $item['crap'], + ] + ); + + foreach ($item['methods'] as $method) { + $buffer .= $this->renderFunctionOrMethodItem( + $methodItemTemplate, + $method, + ' ' + ); + } + } + + return $buffer; + } + + protected function renderFunctionItems(array $functions, \Text_Template $template): string + { + if (empty($functions)) { + return ''; + } + + $buffer = ''; + + foreach ($functions as $function) { + $buffer .= $this->renderFunctionOrMethodItem( + $template, + $function + ); + } + + return $buffer; + } + + protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, string $indent = ''): string + { + $numMethods = 0; + $numTestedMethods = 0; + + if ($item['executableLines'] > 0) { + $numMethods = 1; + + if ($item['executedLines'] === $item['executableLines']) { + $numTestedMethods = 1; + } + } + + return $this->renderItemTemplate( + $template, + [ + 'name' => \sprintf( + '%s%s', + $indent, + $item['startLine'], + \htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), + $item['functionName'] ?? $item['methodName'] + ), + 'numMethods' => $numMethods, + 'numTestedMethods' => $numTestedMethods, + 'linesExecutedPercent' => Util::percent( + $item['executedLines'], + $item['executableLines'] + ), + 'linesExecutedPercentAsString' => Util::percent( + $item['executedLines'], + $item['executableLines'], + true + ), + 'numExecutedLines' => $item['executedLines'], + 'numExecutableLines' => $item['executableLines'], + 'testedMethodsPercent' => Util::percent( + $numTestedMethods, + 1 + ), + 'testedMethodsPercentAsString' => Util::percent( + $numTestedMethods, + 1, + true + ), + 'crap' => $item['crap'], + ] + ); + } + + protected function renderSource(FileNode $node): string + { + $coverageData = $node->getCoverageData(); + $testData = $node->getTestData(); + $codeLines = $this->loadFile($node->getPath()); + $lines = ''; + $i = 1; + + foreach ($codeLines as $line) { + $trClass = ''; + $popoverContent = ''; + $popoverTitle = ''; + + if (\array_key_exists($i, $coverageData)) { + $numTests = ($coverageData[$i] ? \count($coverageData[$i]) : 0); + + if ($coverageData[$i] === null) { + $trClass = ' class="warning"'; + } elseif ($numTests == 0) { + $trClass = ' class="danger"'; + } else { + $lineCss = 'covered-by-large-tests'; + $popoverContent = '
      '; + + if ($numTests > 1) { + $popoverTitle = $numTests . ' tests cover line ' . $i; + } else { + $popoverTitle = '1 test covers line ' . $i; + } + + foreach ($coverageData[$i] as $test) { + if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') { + $lineCss = 'covered-by-medium-tests'; + } elseif ($testData[$test]['size'] == 'small') { + $lineCss = 'covered-by-small-tests'; + } + + switch ($testData[$test]['status']) { + case 0: + switch ($testData[$test]['size']) { + case 'small': + $testCSS = ' class="covered-by-small-tests"'; + + break; + + case 'medium': + $testCSS = ' class="covered-by-medium-tests"'; + + break; + + default: + $testCSS = ' class="covered-by-large-tests"'; + + break; + } + + break; + + case 1: + case 2: + $testCSS = ' class="warning"'; + + break; + + case 3: + $testCSS = ' class="danger"'; + + break; + + case 4: + $testCSS = ' class="danger"'; + + break; + + default: + $testCSS = ''; + } + + $popoverContent .= \sprintf( + '%s', + $testCSS, + \htmlspecialchars($test, $this->htmlSpecialCharsFlags) + ); + } + + $popoverContent .= '
    '; + $trClass = ' class="' . $lineCss . ' popin"'; + } + } + + $popover = ''; + + if (!empty($popoverTitle)) { + $popover = \sprintf( + ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"', + $popoverTitle, + \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags) + ); + } + + $lines .= \sprintf( + ' %s' . "\n", + $trClass, + $popover, + $i, + $i, + $i, + $line + ); + + $i++; + } + + return $lines; + } + + /** + * @param string $file + */ + protected function loadFile($file): array + { + $buffer = \file_get_contents($file); + $tokens = \token_get_all($buffer); + $result = ['']; + $i = 0; + $stringFlag = false; + $fileEndsWithNewLine = \substr($buffer, -1) == "\n"; + + unset($buffer); + + foreach ($tokens as $j => $token) { + if (\is_string($token)) { + if ($token === '"' && $tokens[$j - 1] !== '\\') { + $result[$i] .= \sprintf( + '%s', + \htmlspecialchars($token, $this->htmlSpecialCharsFlags) + ); + + $stringFlag = !$stringFlag; + } else { + $result[$i] .= \sprintf( + '%s', + \htmlspecialchars($token, $this->htmlSpecialCharsFlags) + ); + } + + continue; + } + + [$token, $value] = $token; + + $value = \str_replace( + ["\t", ' '], + ['    ', ' '], + \htmlspecialchars($value, $this->htmlSpecialCharsFlags) + ); + + if ($value === "\n") { + $result[++$i] = ''; + } else { + $lines = \explode("\n", $value); + + foreach ($lines as $jj => $line) { + $line = \trim($line); + + if ($line !== '') { + if ($stringFlag) { + $colour = 'string'; + } else { + switch ($token) { + case \T_INLINE_HTML: + $colour = 'html'; + + break; + + case \T_COMMENT: + case \T_DOC_COMMENT: + $colour = 'comment'; + + break; + + case \T_ABSTRACT: + case \T_ARRAY: + case \T_AS: + case \T_BREAK: + case \T_CALLABLE: + case \T_CASE: + case \T_CATCH: + case \T_CLASS: + case \T_CLONE: + case \T_CONTINUE: + case \T_DEFAULT: + case \T_ECHO: + case \T_ELSE: + case \T_ELSEIF: + case \T_EMPTY: + case \T_ENDDECLARE: + case \T_ENDFOR: + case \T_ENDFOREACH: + case \T_ENDIF: + case \T_ENDSWITCH: + case \T_ENDWHILE: + case \T_EXIT: + case \T_EXTENDS: + case \T_FINAL: + case \T_FINALLY: + case \T_FOREACH: + case \T_FUNCTION: + case \T_GLOBAL: + case \T_IF: + case \T_IMPLEMENTS: + case \T_INCLUDE: + case \T_INCLUDE_ONCE: + case \T_INSTANCEOF: + case \T_INSTEADOF: + case \T_INTERFACE: + case \T_ISSET: + case \T_LOGICAL_AND: + case \T_LOGICAL_OR: + case \T_LOGICAL_XOR: + case \T_NAMESPACE: + case \T_NEW: + case \T_PRIVATE: + case \T_PROTECTED: + case \T_PUBLIC: + case \T_REQUIRE: + case \T_REQUIRE_ONCE: + case \T_RETURN: + case \T_STATIC: + case \T_THROW: + case \T_TRAIT: + case \T_TRY: + case \T_UNSET: + case \T_USE: + case \T_VAR: + case \T_WHILE: + case \T_YIELD: + $colour = 'keyword'; + + break; + + default: + $colour = 'default'; + } + } + + $result[$i] .= \sprintf( + '%s', + $colour, + $line + ); + } + + if (isset($lines[$jj + 1])) { + $result[++$i] = ''; + } + } + } + } + + if ($fileEndsWithNewLine) { + unset($result[\count($result) - 1]); + } + + return $result; + } + + private function abbreviateClassName(string $className): string + { + $tmp = \explode('\\', $className); + + if (\count($tmp) > 1) { + $className = \sprintf( + '%s', + $className, + \array_pop($tmp) + ); + } + + return $className; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\CodeCoverage\RuntimeException; + +/** + * Generates an HTML report from a code coverage object. + */ +final class Facade +{ + /** + * @var string + */ + private $templatePath; + + /** + * @var string + */ + private $generator; + + /** + * @var int + */ + private $lowUpperBound; + + /** + * @var int + */ + private $highLowerBound; + + public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, string $generator = '') + { + $this->generator = $generator; + $this->highLowerBound = $highLowerBound; + $this->lowUpperBound = $lowUpperBound; + $this->templatePath = __DIR__ . '/Renderer/Template/'; + } + + /** + * @throws RuntimeException + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + public function process(CodeCoverage $coverage, string $target): void + { + $target = $this->getDirectory($target); + $report = $coverage->getReport(); + + if (!isset($_SERVER['REQUEST_TIME'])) { + $_SERVER['REQUEST_TIME'] = \time(); + } + + $date = \date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); + + $dashboard = new Dashboard( + $this->templatePath, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound + ); + + $directory = new Directory( + $this->templatePath, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound + ); + + $file = new File( + $this->templatePath, + $this->generator, + $date, + $this->lowUpperBound, + $this->highLowerBound + ); + + $directory->render($report, $target . 'index.html'); + $dashboard->render($report, $target . 'dashboard.html'); + + foreach ($report as $node) { + $id = $node->getId(); + + if ($node instanceof DirectoryNode) { + if (!$this->createDirectory($target . $id)) { + throw new \RuntimeException(\sprintf('Directory "%s" was not created', $target . $id)); + } + + $directory->render($node, $target . $id . '/index.html'); + $dashboard->render($node, $target . $id . '/dashboard.html'); + } else { + $dir = \dirname($target . $id); + + if (!$this->createDirectory($dir)) { + throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dir)); + } + + $file->render($node, $target . $id . '.html'); + } + } + + $this->copyFiles($target); + } + + /** + * @throws RuntimeException + */ + private function copyFiles(string $target): void + { + $dir = $this->getDirectory($target . '.css'); + + \copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); + \copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); + \copy($this->templatePath . 'css/style.css', $dir . 'style.css'); + \copy($this->templatePath . 'css/custom.css', $dir . 'custom.css'); + \copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); + + $dir = $this->getDirectory($target . '.icons'); + \copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); + \copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg'); + + $dir = $this->getDirectory($target . '.js'); + \copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); + \copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js'); + \copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); + \copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); + \copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); + \copy($this->templatePath . 'js/file.js', $dir . 'file.js'); + } + + /** + * @throws RuntimeException + */ + private function getDirectory(string $directory): string + { + if (\substr($directory, -1, 1) != \DIRECTORY_SEPARATOR) { + $directory .= \DIRECTORY_SEPARATOR; + } + + if (!$this->createDirectory($directory)) { + throw new RuntimeException( + \sprintf( + 'Directory "%s" does not exist.', + $directory + ) + ); + } + + return $directory; + } + + private function createDirectory(string $directory): bool + { + return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report\Html; + +use SebastianBergmann\CodeCoverage\Node\AbstractNode; +use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; +use SebastianBergmann\CodeCoverage\Node\File as FileNode; +use SebastianBergmann\CodeCoverage\Version; +use SebastianBergmann\Environment\Runtime; + +/** + * Base class for node renderers. + */ +abstract class Renderer +{ + /** + * @var string + */ + protected $templatePath; + + /** + * @var string + */ + protected $generator; + + /** + * @var string + */ + protected $date; + + /** + * @var int + */ + protected $lowUpperBound; + + /** + * @var int + */ + protected $highLowerBound; + + /** + * @var string + */ + protected $version; + + public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound) + { + $this->templatePath = $templatePath; + $this->generator = $generator; + $this->date = $date; + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->version = Version::id(); + } + + protected function renderItemTemplate(\Text_Template $template, array $data): string + { + $numSeparator = ' / '; + + if (isset($data['numClasses']) && $data['numClasses'] > 0) { + $classesLevel = $this->getColorLevel($data['testedClassesPercent']); + + $classesNumber = $data['numTestedClasses'] . $numSeparator . + $data['numClasses']; + + $classesBar = $this->getCoverageBar( + $data['testedClassesPercent'] + ); + } else { + $classesLevel = ''; + $classesNumber = '0' . $numSeparator . '0'; + $classesBar = ''; + $data['testedClassesPercentAsString'] = 'n/a'; + } + + if ($data['numMethods'] > 0) { + $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); + + $methodsNumber = $data['numTestedMethods'] . $numSeparator . + $data['numMethods']; + + $methodsBar = $this->getCoverageBar( + $data['testedMethodsPercent'] + ); + } else { + $methodsLevel = ''; + $methodsNumber = '0' . $numSeparator . '0'; + $methodsBar = ''; + $data['testedMethodsPercentAsString'] = 'n/a'; + } + + if ($data['numExecutableLines'] > 0) { + $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); + + $linesNumber = $data['numExecutedLines'] . $numSeparator . + $data['numExecutableLines']; + + $linesBar = $this->getCoverageBar( + $data['linesExecutedPercent'] + ); + } else { + $linesLevel = ''; + $linesNumber = '0' . $numSeparator . '0'; + $linesBar = ''; + $data['linesExecutedPercentAsString'] = 'n/a'; + } + + $template->setVar( + [ + 'icon' => $data['icon'] ?? '', + 'crap' => $data['crap'] ?? '', + 'name' => $data['name'], + 'lines_bar' => $linesBar, + 'lines_executed_percent' => $data['linesExecutedPercentAsString'], + 'lines_level' => $linesLevel, + 'lines_number' => $linesNumber, + 'methods_bar' => $methodsBar, + 'methods_tested_percent' => $data['testedMethodsPercentAsString'], + 'methods_level' => $methodsLevel, + 'methods_number' => $methodsNumber, + 'classes_bar' => $classesBar, + 'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '', + 'classes_level' => $classesLevel, + 'classes_number' => $classesNumber, + ] + ); + + return $template->render(); + } + + protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node): void + { + $template->setVar( + [ + 'id' => $node->getId(), + 'full_path' => $node->getPath(), + 'path_to_root' => $this->getPathToRoot($node), + 'breadcrumbs' => $this->getBreadcrumbs($node), + 'date' => $this->date, + 'version' => $this->version, + 'runtime' => $this->getRuntimeString(), + 'generator' => $this->generator, + 'low_upper_bound' => $this->lowUpperBound, + 'high_lower_bound' => $this->highLowerBound, + ] + ); + } + + protected function getBreadcrumbs(AbstractNode $node): string + { + $breadcrumbs = ''; + $path = $node->getPathAsArray(); + $pathToRoot = []; + $max = \count($path); + + if ($node instanceof FileNode) { + $max--; + } + + for ($i = 0; $i < $max; $i++) { + $pathToRoot[] = \str_repeat('../', $i); + } + + foreach ($path as $step) { + if ($step !== $node) { + $breadcrumbs .= $this->getInactiveBreadcrumb( + $step, + \array_pop($pathToRoot) + ); + } else { + $breadcrumbs .= $this->getActiveBreadcrumb($step); + } + } + + return $breadcrumbs; + } + + protected function getActiveBreadcrumb(AbstractNode $node): string + { + $buffer = \sprintf( + ' ' . "\n", + $node->getName() + ); + + if ($node instanceof DirectoryNode) { + $buffer .= ' ' . "\n"; + } + + return $buffer; + } + + protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string + { + return \sprintf( + ' ' . "\n", + $pathToRoot, + $node->getName() + ); + } + + protected function getPathToRoot(AbstractNode $node): string + { + $id = $node->getId(); + $depth = \substr_count($id, '/'); + + if ($id !== 'index' && + $node instanceof DirectoryNode) { + $depth++; + } + + return \str_repeat('../', $depth); + } + + protected function getCoverageBar(float $percent): string + { + $level = $this->getColorLevel($percent); + + $template = new \Text_Template( + $this->templatePath . 'coverage_bar.html', + '{{', + '}}' + ); + + $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]); + + return $template->render(); + } + + protected function getColorLevel(float $percent): string + { + if ($percent <= $this->lowUpperBound) { + return 'danger'; + } + + if ($percent > $this->lowUpperBound && + $percent < $this->highLowerBound) { + return 'warning'; + } + + return 'success'; + } + + private function getRuntimeString(): string + { + $runtime = new Runtime; + + $buffer = \sprintf( + '%s %s', + $runtime->getVendorUrl(), + $runtime->getName(), + $runtime->getVersion() + ); + + if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) { + $buffer .= \sprintf( + ' with Xdebug %s', + \phpversion('xdebug') + ); + } + + return $buffer; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\Util; + +/** + * Generates human readable output from a code coverage object. + * + * The output gets put into a text file our written to the CLI. + */ +final class Text +{ + /** + * @var string + */ + private const COLOR_GREEN = "\x1b[30;42m"; + + /** + * @var string + */ + private const COLOR_YELLOW = "\x1b[30;43m"; + + /** + * @var string + */ + private const COLOR_RED = "\x1b[37;41m"; + + /** + * @var string + */ + private const COLOR_HEADER = "\x1b[1;37;40m"; + + /** + * @var string + */ + private const COLOR_RESET = "\x1b[0m"; + + /** + * @var string + */ + private const COLOR_EOL = "\x1b[2K"; + + /** + * @var int + */ + private $lowUpperBound; + + /** + * @var int + */ + private $highLowerBound; + + /** + * @var bool + */ + private $showUncoveredFiles; + + /** + * @var bool + */ + private $showOnlySummary; + + public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false) + { + $this->lowUpperBound = $lowUpperBound; + $this->highLowerBound = $highLowerBound; + $this->showUncoveredFiles = $showUncoveredFiles; + $this->showOnlySummary = $showOnlySummary; + } + + public function process(CodeCoverage $coverage, bool $showColors = false): string + { + $output = \PHP_EOL . \PHP_EOL; + $report = $coverage->getReport(); + + $colors = [ + 'header' => '', + 'classes' => '', + 'methods' => '', + 'lines' => '', + 'reset' => '', + 'eol' => '', + ]; + + if ($showColors) { + $colors['classes'] = $this->getCoverageColor( + $report->getNumTestedClassesAndTraits(), + $report->getNumClassesAndTraits() + ); + + $colors['methods'] = $this->getCoverageColor( + $report->getNumTestedMethods(), + $report->getNumMethods() + ); + + $colors['lines'] = $this->getCoverageColor( + $report->getNumExecutedLines(), + $report->getNumExecutableLines() + ); + + $colors['reset'] = self::COLOR_RESET; + $colors['header'] = self::COLOR_HEADER; + $colors['eol'] = self::COLOR_EOL; + } + + $classes = \sprintf( + ' Classes: %6s (%d/%d)', + Util::percent( + $report->getNumTestedClassesAndTraits(), + $report->getNumClassesAndTraits(), + true + ), + $report->getNumTestedClassesAndTraits(), + $report->getNumClassesAndTraits() + ); + + $methods = \sprintf( + ' Methods: %6s (%d/%d)', + Util::percent( + $report->getNumTestedMethods(), + $report->getNumMethods(), + true + ), + $report->getNumTestedMethods(), + $report->getNumMethods() + ); + + $lines = \sprintf( + ' Lines: %6s (%d/%d)', + Util::percent( + $report->getNumExecutedLines(), + $report->getNumExecutableLines(), + true + ), + $report->getNumExecutedLines(), + $report->getNumExecutableLines() + ); + + $padding = \max(\array_map('strlen', [$classes, $methods, $lines])); + + if ($this->showOnlySummary) { + $title = 'Code Coverage Report Summary:'; + $padding = \max($padding, \strlen($title)); + + $output .= $this->format($colors['header'], $padding, $title); + } else { + $date = \date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']); + $title = 'Code Coverage Report:'; + + $output .= $this->format($colors['header'], $padding, $title); + $output .= $this->format($colors['header'], $padding, $date); + $output .= $this->format($colors['header'], $padding, ''); + $output .= $this->format($colors['header'], $padding, ' Summary:'); + } + + $output .= $this->format($colors['classes'], $padding, $classes); + $output .= $this->format($colors['methods'], $padding, $methods); + $output .= $this->format($colors['lines'], $padding, $lines); + + if ($this->showOnlySummary) { + return $output . \PHP_EOL; + } + + $classCoverage = []; + + foreach ($report as $item) { + if (!$item instanceof File) { + continue; + } + + $classes = $item->getClassesAndTraits(); + + foreach ($classes as $className => $class) { + $classStatements = 0; + $coveredClassStatements = 0; + $coveredMethods = 0; + $classMethods = 0; + + foreach ($class['methods'] as $method) { + if ($method['executableLines'] == 0) { + continue; + } + + $classMethods++; + $classStatements += $method['executableLines']; + $coveredClassStatements += $method['executedLines']; + + if ($method['coverage'] == 100) { + $coveredMethods++; + } + } + + $namespace = ''; + + if (!empty($class['package']['namespace'])) { + $namespace = '\\' . $class['package']['namespace'] . '::'; + } elseif (!empty($class['package']['fullPackage'])) { + $namespace = '@' . $class['package']['fullPackage'] . '::'; + } + + $classCoverage[$namespace . $className] = [ + 'namespace' => $namespace, + 'className ' => $className, + 'methodsCovered' => $coveredMethods, + 'methodCount' => $classMethods, + 'statementsCovered' => $coveredClassStatements, + 'statementCount' => $classStatements, + ]; + } + } + + \ksort($classCoverage); + + $methodColor = ''; + $linesColor = ''; + $resetColor = ''; + + foreach ($classCoverage as $fullQualifiedPath => $classInfo) { + if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) { + if ($showColors) { + $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); + $linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); + $resetColor = $colors['reset']; + } + + $output .= \PHP_EOL . $fullQualifiedPath . \PHP_EOL + . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ' + . ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor; + } + } + + return $output . \PHP_EOL; + } + + private function getCoverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string + { + $coverage = Util::percent( + $numberOfCoveredElements, + $totalNumberOfElements + ); + + if ($coverage >= $this->highLowerBound) { + return self::COLOR_GREEN; + } + + if ($coverage > $this->lowUpperBound) { + return self::COLOR_YELLOW; + } + + return self::COLOR_RED; + } + + private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string + { + $format = '%' . $precision . 's'; + + return Util::percent( + $numberOfCoveredElements, + $totalNumberOfElements, + true, + true + ) . + ' (' . \sprintf($format, $numberOfCoveredElements) . '/' . + \sprintf($format, $totalNumberOfElements) . ')'; + } + + private function format($color, $padding, $string): string + { + $reset = $color ? self::COLOR_RESET : ''; + + return $color . \str_pad($string, $padding) . $reset . \PHP_EOL; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Report; + +use SebastianBergmann\CodeCoverage\CodeCoverage; +use SebastianBergmann\CodeCoverage\Node\File; +use SebastianBergmann\CodeCoverage\RuntimeException; + +final class Crap4j +{ + /** + * @var int + */ + private $threshold; + + public function __construct(int $threshold = 30) + { + $this->threshold = $threshold; + } + + /** + * @throws \RuntimeException + */ + public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string + { + $document = new \DOMDocument('1.0', 'UTF-8'); + $document->formatOutput = true; + + $root = $document->createElement('crap_result'); + $document->appendChild($root); + + $project = $document->createElement('project', \is_string($name) ? $name : ''); + $root->appendChild($project); + $root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s', (int) $_SERVER['REQUEST_TIME']))); + + $stats = $document->createElement('stats'); + $methodsNode = $document->createElement('methods'); + + $report = $coverage->getReport(); + unset($coverage); + + $fullMethodCount = 0; + $fullCrapMethodCount = 0; + $fullCrapLoad = 0; + $fullCrap = 0; + + foreach ($report as $item) { + $namespace = 'global'; + + if (!$item instanceof File) { + continue; + } + + $file = $document->createElement('file'); + $file->setAttribute('name', $item->getPath()); + + $classes = $item->getClassesAndTraits(); + + foreach ($classes as $className => $class) { + foreach ($class['methods'] as $methodName => $method) { + $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']); + + $fullCrap += $method['crap']; + $fullCrapLoad += $crapLoad; + $fullMethodCount++; + + if ($method['crap'] >= $this->threshold) { + $fullCrapMethodCount++; + } + + $methodNode = $document->createElement('method'); + + if (!empty($class['package']['namespace'])) { + $namespace = $class['package']['namespace']; + } + + $methodNode->appendChild($document->createElement('package', $namespace)); + $methodNode->appendChild($document->createElement('className', $className)); + $methodNode->appendChild($document->createElement('methodName', $methodName)); + $methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature']))); + $methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature']))); + $methodNode->appendChild($document->createElement('crap', $this->roundValue($method['crap']))); + $methodNode->appendChild($document->createElement('complexity', $method['ccn'])); + $methodNode->appendChild($document->createElement('coverage', $this->roundValue($method['coverage']))); + $methodNode->appendChild($document->createElement('crapLoad', \round($crapLoad))); + + $methodsNode->appendChild($methodNode); + } + } + } + + $stats->appendChild($document->createElement('name', 'Method Crap Stats')); + $stats->appendChild($document->createElement('methodCount', $fullMethodCount)); + $stats->appendChild($document->createElement('crapMethodCount', $fullCrapMethodCount)); + $stats->appendChild($document->createElement('crapLoad', \round($fullCrapLoad))); + $stats->appendChild($document->createElement('totalCrap', $fullCrap)); + + $crapMethodPercent = 0; + + if ($fullMethodCount > 0) { + $crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount); + } + + $stats->appendChild($document->createElement('crapMethodPercent', $crapMethodPercent)); + + $root->appendChild($stats); + $root->appendChild($methodsNode); + + $buffer = $document->saveXML(); + + if ($target !== null) { + if (!$this->createDirectory(\dirname($target))) { + throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); + } + + if (@\file_put_contents($target, $buffer) === false) { + throw new RuntimeException( + \sprintf( + 'Could not write to "%s', + $target + ) + ); + } + } + + return $buffer; + } + + /** + * @param float $crapValue + * @param int $cyclomaticComplexity + * @param float $coveragePercent + */ + private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent): float + { + $crapLoad = 0; + + if ($crapValue >= $this->threshold) { + $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100); + $crapLoad += $cyclomaticComplexity / $this->threshold; + } + + return $crapLoad; + } + + /** + * @param float $value + */ + private function roundValue($value): float + { + return \round($value, 2); + } + + private function createDirectory(string $directory): bool + { + return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use PHPUnit\Framework\TestCase; +use PHPUnit\Runner\PhptTestCase; +use PHPUnit\Util\Test; +use SebastianBergmann\CodeCoverage\Driver\Driver; +use SebastianBergmann\CodeCoverage\Driver\PHPDBG; +use SebastianBergmann\CodeCoverage\Driver\Xdebug; +use SebastianBergmann\CodeCoverage\Node\Builder; +use SebastianBergmann\CodeCoverage\Node\Directory; +use SebastianBergmann\CodeUnitReverseLookup\Wizard; +use SebastianBergmann\Environment\Runtime; + +/** + * Provides collection functionality for PHP code coverage information. + */ +final class CodeCoverage +{ + /** + * @var Driver + */ + private $driver; + + /** + * @var Filter + */ + private $filter; + + /** + * @var Wizard + */ + private $wizard; + + /** + * @var bool + */ + private $cacheTokens = false; + + /** + * @var bool + */ + private $checkForUnintentionallyCoveredCode = false; + + /** + * @var bool + */ + private $forceCoversAnnotation = false; + + /** + * @var bool + */ + private $checkForUnexecutedCoveredCode = false; + + /** + * @var bool + */ + private $checkForMissingCoversAnnotation = false; + + /** + * @var bool + */ + private $addUncoveredFilesFromWhitelist = true; + + /** + * @var bool + */ + private $processUncoveredFilesFromWhitelist = false; + + /** + * @var bool + */ + private $ignoreDeprecatedCode = false; + + /** + * @var PhptTestCase|string|TestCase + */ + private $currentId; + + /** + * Code coverage data. + * + * @var array + */ + private $data = []; + + /** + * @var array + */ + private $ignoredLines = []; + + /** + * @var bool + */ + private $disableIgnoredLines = false; + + /** + * Test data. + * + * @var array + */ + private $tests = []; + + /** + * @var string[] + */ + private $unintentionallyCoveredSubclassesWhitelist = []; + + /** + * Determine if the data has been initialized or not + * + * @var bool + */ + private $isInitialized = false; + + /** + * Determine whether we need to check for dead and unused code on each test + * + * @var bool + */ + private $shouldCheckForDeadAndUnused = true; + + /** + * @var Directory + */ + private $report; + + /** + * @throws RuntimeException + */ + public function __construct(Driver $driver = null, Filter $filter = null) + { + if ($filter === null) { + $filter = new Filter; + } + + if ($driver === null) { + $driver = $this->selectDriver($filter); + } + + $this->driver = $driver; + $this->filter = $filter; + + $this->wizard = new Wizard; + } + + /** + * Returns the code coverage information as a graph of node objects. + */ + public function getReport(): Directory + { + if ($this->report === null) { + $builder = new Builder; + + $this->report = $builder->build($this); + } + + return $this->report; + } + + /** + * Clears collected code coverage data. + */ + public function clear(): void + { + $this->isInitialized = false; + $this->currentId = null; + $this->data = []; + $this->tests = []; + $this->report = null; + } + + /** + * Returns the filter object used. + */ + public function filter(): Filter + { + return $this->filter; + } + + /** + * Returns the collected code coverage data. + */ + public function getData(bool $raw = false): array + { + if (!$raw && $this->addUncoveredFilesFromWhitelist) { + $this->addUncoveredFilesFromWhitelist(); + } + + return $this->data; + } + + /** + * Sets the coverage data. + */ + public function setData(array $data): void + { + $this->data = $data; + $this->report = null; + } + + /** + * Returns the test data. + */ + public function getTests(): array + { + return $this->tests; + } + + /** + * Sets the test data. + */ + public function setTests(array $tests): void + { + $this->tests = $tests; + } + + /** + * Start collection of code coverage information. + * + * @param PhptTestCase|string|TestCase $id + * + * @throws RuntimeException + */ + public function start($id, bool $clear = false): void + { + if ($clear) { + $this->clear(); + } + + if ($this->isInitialized === false) { + $this->initializeData(); + } + + $this->currentId = $id; + + $this->driver->start($this->shouldCheckForDeadAndUnused); + } + + /** + * Stop collection of code coverage information. + * + * @param array|false $linesToBeCovered + * + * @throws MissingCoversAnnotationException + * @throws CoveredCodeNotExecutedException + * @throws RuntimeException + * @throws InvalidArgumentException + * @throws \ReflectionException + */ + public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): array + { + if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { + throw InvalidArgumentException::create( + 2, + 'array or false' + ); + } + + $data = $this->driver->stop(); + $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation); + + $this->currentId = null; + + return $data; + } + + /** + * Appends code coverage data. + * + * @param PhptTestCase|string|TestCase $id + * @param array|false $linesToBeCovered + * + * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException + * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException + * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException + * @throws \ReflectionException + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + * @throws RuntimeException + */ + public function append(array $data, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void + { + if ($id === null) { + $id = $this->currentId; + } + + if ($id === null) { + throw new RuntimeException; + } + + $this->applyWhitelistFilter($data); + $this->applyIgnoredLinesFilter($data); + $this->initializeFilesThatAreSeenTheFirstTime($data); + + if (!$append) { + return; + } + + if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { + $this->applyCoversAnnotationFilter( + $data, + $linesToBeCovered, + $linesToBeUsed, + $ignoreForceCoversAnnotation + ); + } + + if (empty($data)) { + return; + } + + $size = 'unknown'; + $status = -1; + + if ($id instanceof TestCase) { + $_size = $id->getSize(); + + if ($_size === Test::SMALL) { + $size = 'small'; + } elseif ($_size === Test::MEDIUM) { + $size = 'medium'; + } elseif ($_size === Test::LARGE) { + $size = 'large'; + } + + $status = $id->getStatus(); + $id = \get_class($id) . '::' . $id->getName(); + } elseif ($id instanceof PhptTestCase) { + $size = 'large'; + $id = $id->getName(); + } + + $this->tests[$id] = ['size' => $size, 'status' => $status]; + + foreach ($data as $file => $lines) { + if (!$this->filter->isFile($file)) { + continue; + } + + foreach ($lines as $k => $v) { + if ($v === Driver::LINE_EXECUTED) { + if (empty($this->data[$file][$k]) || !\in_array($id, $this->data[$file][$k])) { + $this->data[$file][$k][] = $id; + } + } + } + } + + $this->report = null; + } + + /** + * Merges the data from another instance. + * + * @param CodeCoverage $that + */ + public function merge(self $that): void + { + $this->filter->setWhitelistedFiles( + \array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) + ); + + foreach ($that->data as $file => $lines) { + if (!isset($this->data[$file])) { + if (!$this->filter->isFiltered($file)) { + $this->data[$file] = $lines; + } + + continue; + } + + // we should compare the lines if any of two contains data + $compareLineNumbers = \array_unique( + \array_merge( + \array_keys($this->data[$file]), + \array_keys($that->data[$file]) + ) + ); + + foreach ($compareLineNumbers as $line) { + $thatPriority = $this->getLinePriority($that->data[$file], $line); + $thisPriority = $this->getLinePriority($this->data[$file], $line); + + if ($thatPriority > $thisPriority) { + $this->data[$file][$line] = $that->data[$file][$line]; + } elseif ($thatPriority === $thisPriority && \is_array($this->data[$file][$line])) { + $this->data[$file][$line] = \array_unique( + \array_merge($this->data[$file][$line], $that->data[$file][$line]) + ); + } + } + } + + $this->tests = \array_merge($this->tests, $that->getTests()); + $this->report = null; + } + + public function setCacheTokens(bool $flag): void + { + $this->cacheTokens = $flag; + } + + public function getCacheTokens(): bool + { + return $this->cacheTokens; + } + + public function setCheckForUnintentionallyCoveredCode(bool $flag): void + { + $this->checkForUnintentionallyCoveredCode = $flag; + } + + public function setForceCoversAnnotation(bool $flag): void + { + $this->forceCoversAnnotation = $flag; + } + + public function setCheckForMissingCoversAnnotation(bool $flag): void + { + $this->checkForMissingCoversAnnotation = $flag; + } + + public function setCheckForUnexecutedCoveredCode(bool $flag): void + { + $this->checkForUnexecutedCoveredCode = $flag; + } + + public function setAddUncoveredFilesFromWhitelist(bool $flag): void + { + $this->addUncoveredFilesFromWhitelist = $flag; + } + + public function setProcessUncoveredFilesFromWhitelist(bool $flag): void + { + $this->processUncoveredFilesFromWhitelist = $flag; + } + + public function setDisableIgnoredLines(bool $flag): void + { + $this->disableIgnoredLines = $flag; + } + + public function setIgnoreDeprecatedCode(bool $flag): void + { + $this->ignoreDeprecatedCode = $flag; + } + + public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist): void + { + $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; + } + + /** + * Determine the priority for a line + * + * 1 = the line is not set + * 2 = the line has not been tested + * 3 = the line is dead code + * 4 = the line has been tested + * + * During a merge, a higher number is better. + * + * @param array $data + * @param int $line + * + * @return int + */ + private function getLinePriority($data, $line) + { + if (!\array_key_exists($line, $data)) { + return 1; + } + + if (\is_array($data[$line]) && \count($data[$line]) === 0) { + return 2; + } + + if ($data[$line] === null) { + return 3; + } + + return 4; + } + + /** + * Applies the @covers annotation filtering. + * + * @param array|false $linesToBeCovered + * + * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException + * @throws \ReflectionException + * @throws MissingCoversAnnotationException + * @throws UnintentionallyCoveredCodeException + */ + private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void + { + if ($linesToBeCovered === false || + ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { + if ($this->checkForMissingCoversAnnotation) { + throw new MissingCoversAnnotationException; + } + + $data = []; + + return; + } + + if (empty($linesToBeCovered)) { + return; + } + + if ($this->checkForUnintentionallyCoveredCode && + (!$this->currentId instanceof TestCase || + (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { + $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + } + + if ($this->checkForUnexecutedCoveredCode) { + $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); + } + + $data = \array_intersect_key($data, $linesToBeCovered); + + foreach (\array_keys($data) as $filename) { + $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); + $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); + } + } + + private function applyWhitelistFilter(array &$data): void + { + foreach (\array_keys($data) as $filename) { + if ($this->filter->isFiltered($filename)) { + unset($data[$filename]); + } + } + } + + /** + * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException + */ + private function applyIgnoredLinesFilter(array &$data): void + { + foreach (\array_keys($data) as $filename) { + if (!$this->filter->isFile($filename)) { + continue; + } + + foreach ($this->getLinesToBeIgnored($filename) as $line) { + unset($data[$filename][$line]); + } + } + } + + private function initializeFilesThatAreSeenTheFirstTime(array $data): void + { + foreach ($data as $file => $lines) { + if (!isset($this->data[$file]) && $this->filter->isFile($file)) { + $this->data[$file] = []; + + foreach ($lines as $k => $v) { + $this->data[$file][$k] = $v === -2 ? null : []; + } + } + } + } + + /** + * @throws CoveredCodeNotExecutedException + * @throws InvalidArgumentException + * @throws MissingCoversAnnotationException + * @throws RuntimeException + * @throws UnintentionallyCoveredCodeException + * @throws \ReflectionException + */ + private function addUncoveredFilesFromWhitelist(): void + { + $data = []; + $uncoveredFiles = \array_diff( + $this->filter->getWhitelist(), + \array_keys($this->data) + ); + + foreach ($uncoveredFiles as $uncoveredFile) { + if (!\file_exists($uncoveredFile)) { + continue; + } + + $data[$uncoveredFile] = []; + + $lines = \count(\file($uncoveredFile)); + + for ($i = 1; $i <= $lines; $i++) { + $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; + } + } + + $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + } + + private function getLinesToBeIgnored(string $fileName): array + { + if (isset($this->ignoredLines[$fileName])) { + return $this->ignoredLines[$fileName]; + } + + try { + return $this->getLinesToBeIgnoredInner($fileName); + } catch (\OutOfBoundsException $e) { + // This can happen with PHP_Token_Stream if the file is syntactically invalid, + // and probably affects a file that wasn't executed. + return []; + } + } + + private function getLinesToBeIgnoredInner(string $fileName): array + { + $this->ignoredLines[$fileName] = []; + + $lines = \file($fileName); + + foreach ($lines as $index => $line) { + if (!\trim($line)) { + $this->ignoredLines[$fileName][] = $index + 1; + } + } + + if ($this->cacheTokens) { + $tokens = \PHP_Token_Stream_CachingFactory::get($fileName); + } else { + $tokens = new \PHP_Token_Stream($fileName); + } + + foreach ($tokens->getInterfaces() as $interface) { + $interfaceStartLine = $interface['startLine']; + $interfaceEndLine = $interface['endLine']; + + foreach (\range($interfaceStartLine, $interfaceEndLine) as $line) { + $this->ignoredLines[$fileName][] = $line; + } + } + + foreach (\array_merge($tokens->getClasses(), $tokens->getTraits()) as $classOrTrait) { + $classOrTraitStartLine = $classOrTrait['startLine']; + $classOrTraitEndLine = $classOrTrait['endLine']; + + if (empty($classOrTrait['methods'])) { + foreach (\range($classOrTraitStartLine, $classOrTraitEndLine) as $line) { + $this->ignoredLines[$fileName][] = $line; + } + + continue; + } + + $firstMethod = \array_shift($classOrTrait['methods']); + $firstMethodStartLine = $firstMethod['startLine']; + $firstMethodEndLine = $firstMethod['endLine']; + $lastMethodEndLine = $firstMethodEndLine; + + do { + $lastMethod = \array_pop($classOrTrait['methods']); + } while ($lastMethod !== null && 0 === \strpos($lastMethod['signature'], 'anonymousFunction')); + + if ($lastMethod !== null) { + $lastMethodEndLine = $lastMethod['endLine']; + } + + foreach (\range($classOrTraitStartLine, $firstMethodStartLine) as $line) { + $this->ignoredLines[$fileName][] = $line; + } + + foreach (\range($lastMethodEndLine + 1, $classOrTraitEndLine) as $line) { + $this->ignoredLines[$fileName][] = $line; + } + } + + if ($this->disableIgnoredLines) { + $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); + \sort($this->ignoredLines[$fileName]); + + return $this->ignoredLines[$fileName]; + } + + $ignore = false; + $stop = false; + + foreach ($tokens->tokens() as $token) { + switch (\get_class($token)) { + case \PHP_Token_COMMENT::class: + case \PHP_Token_DOC_COMMENT::class: + $_token = \trim($token); + $_line = \trim($lines[$token->getLine() - 1]); + + if ($_token === '// @codeCoverageIgnore' || + $_token === '//@codeCoverageIgnore') { + $ignore = true; + $stop = true; + } elseif ($_token === '// @codeCoverageIgnoreStart' || + $_token === '//@codeCoverageIgnoreStart') { + $ignore = true; + } elseif ($_token === '// @codeCoverageIgnoreEnd' || + $_token === '//@codeCoverageIgnoreEnd') { + $stop = true; + } + + if (!$ignore) { + $start = $token->getLine(); + $end = $start + \substr_count($token, "\n"); + + // Do not ignore the first line when there is a token + // before the comment + if (0 !== \strpos($_token, $_line)) { + $start++; + } + + for ($i = $start; $i < $end; $i++) { + $this->ignoredLines[$fileName][] = $i; + } + + // A DOC_COMMENT token or a COMMENT token starting with "/*" + // does not contain the final \n character in its text + if (isset($lines[$i - 1]) && 0 === \strpos($_token, '/*') && '*/' === \substr(\trim($lines[$i - 1]), -2)) { + $this->ignoredLines[$fileName][] = $i; + } + } + + break; + + case \PHP_Token_INTERFACE::class: + case \PHP_Token_TRAIT::class: + case \PHP_Token_CLASS::class: + case \PHP_Token_FUNCTION::class: + /* @var \PHP_Token_Interface $token */ + + $docblock = $token->getDocblock(); + + $this->ignoredLines[$fileName][] = $token->getLine(); + + if (\strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && \strpos($docblock, '@deprecated'))) { + $endLine = $token->getEndLine(); + + for ($i = $token->getLine(); $i <= $endLine; $i++) { + $this->ignoredLines[$fileName][] = $i; + } + } + + break; + + /* @noinspection PhpMissingBreakStatementInspection */ + case \PHP_Token_NAMESPACE::class: + $this->ignoredLines[$fileName][] = $token->getEndLine(); + + // Intentional fallthrough + case \PHP_Token_DECLARE::class: + case \PHP_Token_OPEN_TAG::class: + case \PHP_Token_CLOSE_TAG::class: + case \PHP_Token_USE::class: + $this->ignoredLines[$fileName][] = $token->getLine(); + + break; + } + + if ($ignore) { + $this->ignoredLines[$fileName][] = $token->getLine(); + + if ($stop) { + $ignore = false; + $stop = false; + } + } + } + + $this->ignoredLines[$fileName][] = \count($lines) + 1; + + $this->ignoredLines[$fileName] = \array_unique( + $this->ignoredLines[$fileName] + ); + + $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); + \sort($this->ignoredLines[$fileName]); + + return $this->ignoredLines[$fileName]; + } + + /** + * @throws \ReflectionException + * @throws UnintentionallyCoveredCodeException + */ + private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void + { + $allowedLines = $this->getAllowedLines( + $linesToBeCovered, + $linesToBeUsed + ); + + $unintentionallyCoveredUnits = []; + + foreach ($data as $file => $_data) { + foreach ($_data as $line => $flag) { + if ($flag === 1 && !isset($allowedLines[$file][$line])) { + $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); + } + } + } + + $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); + + if (!empty($unintentionallyCoveredUnits)) { + throw new UnintentionallyCoveredCodeException( + $unintentionallyCoveredUnits + ); + } + } + + /** + * @throws CoveredCodeNotExecutedException + */ + private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void + { + $executedCodeUnits = $this->coverageToCodeUnits($data); + $message = ''; + + foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { + if (!\in_array($codeUnit, $executedCodeUnits)) { + $message .= \sprintf( + '- %s is expected to be executed (@covers) but was not executed' . "\n", + $codeUnit + ); + } + } + + foreach ($this->linesToCodeUnits($linesToBeUsed) as $codeUnit) { + if (!\in_array($codeUnit, $executedCodeUnits)) { + $message .= \sprintf( + '- %s is expected to be executed (@uses) but was not executed' . "\n", + $codeUnit + ); + } + } + + if (!empty($message)) { + throw new CoveredCodeNotExecutedException($message); + } + } + + private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array + { + $allowedLines = []; + + foreach (\array_keys($linesToBeCovered) as $file) { + if (!isset($allowedLines[$file])) { + $allowedLines[$file] = []; + } + + $allowedLines[$file] = \array_merge( + $allowedLines[$file], + $linesToBeCovered[$file] + ); + } + + foreach (\array_keys($linesToBeUsed) as $file) { + if (!isset($allowedLines[$file])) { + $allowedLines[$file] = []; + } + + $allowedLines[$file] = \array_merge( + $allowedLines[$file], + $linesToBeUsed[$file] + ); + } + + foreach (\array_keys($allowedLines) as $file) { + $allowedLines[$file] = \array_flip( + \array_unique($allowedLines[$file]) + ); + } + + return $allowedLines; + } + + /** + * @throws RuntimeException + */ + private function selectDriver(Filter $filter): Driver + { + $runtime = new Runtime; + + if (!$runtime->canCollectCodeCoverage()) { + throw new RuntimeException('No code coverage driver available'); + } + + if ($runtime->isPHPDBG()) { + return new PHPDBG; + } + + if ($runtime->hasXdebug()) { + return new Xdebug($filter); + } + + throw new RuntimeException('No code coverage driver available'); + } + + private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits): array + { + $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); + \sort($unintentionallyCoveredUnits); + + foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { + $unit = \explode('::', $unintentionallyCoveredUnits[$k]); + + if (\count($unit) !== 2) { + continue; + } + + $class = new \ReflectionClass($unit[0]); + + foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { + if ($class->isSubclassOf($whitelisted)) { + unset($unintentionallyCoveredUnits[$k]); + + break; + } + } + } + + return \array_values($unintentionallyCoveredUnits); + } + + /** + * @throws CoveredCodeNotExecutedException + * @throws InvalidArgumentException + * @throws MissingCoversAnnotationException + * @throws RuntimeException + * @throws UnintentionallyCoveredCodeException + * @throws \ReflectionException + */ + private function initializeData(): void + { + $this->isInitialized = true; + + if ($this->processUncoveredFilesFromWhitelist) { + $this->shouldCheckForDeadAndUnused = false; + + $this->driver->start(); + + foreach ($this->filter->getWhitelist() as $file) { + if ($this->filter->isFile($file)) { + include_once $file; + } + } + + $data = []; + $coverage = $this->driver->stop(); + + foreach ($coverage as $file => $fileCoverage) { + if ($this->filter->isFiltered($file)) { + continue; + } + + foreach (\array_keys($fileCoverage) as $key) { + if ($fileCoverage[$key] === Driver::LINE_EXECUTED) { + $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; + } + } + + $data[$file] = $fileCoverage; + } + + $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); + } + } + + private function coverageToCodeUnits(array $data): array + { + $codeUnits = []; + + foreach ($data as $filename => $lines) { + foreach ($lines as $line => $flag) { + if ($flag === 1) { + $codeUnits[] = $this->wizard->lookup($filename, $line); + } + } + } + + return \array_unique($codeUnits); + } + + private function linesToCodeUnits(array $data): array + { + $codeUnits = []; + + foreach ($data as $filename => $lines) { + foreach ($lines as $line) { + $codeUnits[] = $this->wizard->lookup($filename, $line); + } + } + + return \array_unique($codeUnits); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use SebastianBergmann\Version as VersionId; + +final class Version +{ + /** + * @var string + */ + private static $version; + + public static function id(): string + { + if (self::$version === null) { + $version = new VersionId('6.1.4', \dirname(__DIR__)); + self::$version = $version->getVersion(); + } + + return self::$version; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use SebastianBergmann\CodeCoverage\RuntimeException; + +/** + * Driver for PHPDBG's code coverage functionality. + * + * @codeCoverageIgnore + */ +final class PHPDBG implements Driver +{ + /** + * @throws RuntimeException + */ + public function __construct() + { + if (\PHP_SAPI !== 'phpdbg') { + throw new RuntimeException( + 'This driver requires the PHPDBG SAPI' + ); + } + + if (!\function_exists('phpdbg_start_oplog')) { + throw new RuntimeException( + 'This build of PHPDBG does not support code coverage' + ); + } + } + + /** + * Start collection of code coverage information. + */ + public function start(bool $determineUnusedAndDead = true): void + { + \phpdbg_start_oplog(); + } + + /** + * Stop collection of code coverage information. + */ + public function stop(): array + { + static $fetchedLines = []; + + $dbgData = \phpdbg_end_oplog(); + + if ($fetchedLines == []) { + $sourceLines = \phpdbg_get_executable(); + } else { + $newFiles = \array_diff(\get_included_files(), \array_keys($fetchedLines)); + + $sourceLines = []; + + if ($newFiles) { + $sourceLines = phpdbg_get_executable(['files' => $newFiles]); + } + } + + foreach ($sourceLines as $file => $lines) { + foreach ($lines as $lineNo => $numExecuted) { + $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; + } + } + + $fetchedLines = \array_merge($fetchedLines, $sourceLines); + + return $this->detectExecutedLines($fetchedLines, $dbgData); + } + + /** + * Convert phpdbg based data into the format CodeCoverage expects + */ + private function detectExecutedLines(array $sourceLines, array $dbgData): array + { + foreach ($dbgData as $file => $coveredLines) { + foreach ($coveredLines as $lineNo => $numExecuted) { + // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. + // make sure we only mark lines executed which are actually executable. + if (isset($sourceLines[$file][$lineNo])) { + $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; + } + } + } + + return $sourceLines; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +use SebastianBergmann\CodeCoverage\Filter; +use SebastianBergmann\CodeCoverage\RuntimeException; + +/** + * Driver for Xdebug's code coverage functionality. + * + * @codeCoverageIgnore + */ +final class Xdebug implements Driver +{ + /** + * @var array + */ + private $cacheNumLines = []; + + /** + * @var Filter + */ + private $filter; + + /** + * @throws RuntimeException + */ + public function __construct(Filter $filter = null) + { + if (!\extension_loaded('xdebug')) { + throw new RuntimeException('This driver requires Xdebug'); + } + + if (!\ini_get('xdebug.coverage_enable')) { + throw new RuntimeException('xdebug.coverage_enable=On has to be set in php.ini'); + } + + if ($filter === null) { + $filter = new Filter; + } + + $this->filter = $filter; + } + + /** + * Start collection of code coverage information. + */ + public function start(bool $determineUnusedAndDead = true): void + { + if ($determineUnusedAndDead) { + \xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); + } else { + \xdebug_start_code_coverage(); + } + } + + /** + * Stop collection of code coverage information. + */ + public function stop(): array + { + $data = \xdebug_get_code_coverage(); + + \xdebug_stop_code_coverage(); + + return $this->cleanup($data); + } + + private function cleanup(array $data): array + { + foreach (\array_keys($data) as $file) { + unset($data[$file][0]); + + if (!$this->filter->isFile($file)) { + continue; + } + + $numLines = $this->getNumberOfLinesInFile($file); + + foreach (\array_keys($data[$file]) as $line) { + if ($line > $numLines) { + unset($data[$file][$line]); + } + } + } + + return $data; + } + + private function getNumberOfLinesInFile(string $fileName): int + { + if (!isset($this->cacheNumLines[$fileName])) { + $buffer = \file_get_contents($fileName); + $lines = \substr_count($buffer, "\n"); + + if (\substr($buffer, -1) !== "\n") { + $lines++; + } + + $this->cacheNumLines[$fileName] = $lines; + } + + return $this->cacheNumLines[$fileName]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Driver; + +/** + * Interface for code coverage drivers. + */ +interface Driver +{ + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + public const LINE_EXECUTED = 1; + + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + public const LINE_NOT_EXECUTED = -1; + + /** + * @var int + * + * @see http://xdebug.org/docs/code_coverage + */ + public const LINE_NOT_EXECUTABLE = -2; + + /** + * Start collection of code coverage information. + */ + public function start(bool $determineUnusedAndDead = true): void; + + /** + * Stop collection of code coverage information. + */ + public function stop(): array; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +/** + * Utility methods. + */ +final class Util +{ + /** + * @return float|int|string + */ + public static function percent(float $a, float $b, bool $asString = false, bool $fixedWidth = false) + { + if ($asString && $b == 0) { + return ''; + } + + $percent = 100; + + if ($b > 0) { + $percent = ($a / $b) * 100; + } + + if ($asString) { + $format = $fixedWidth ? '%6.2F%%' : '%01.2F%%'; + + return \sprintf($format, $percent); + } + + return $percent; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Node; + +/** + * Recursive iterator for node object graphs. + */ +final class Iterator implements \RecursiveIterator +{ + /** + * @var int + */ + private $position; + + /** + * @var AbstractNode[] + */ + private $nodes; + + public function __construct(Directory $node) + { + $this->nodes = $node->getChildNodes(); + } + + /** + * Rewinds the Iterator to the first element. + */ + public function rewind(): void + { + $this->position = 0; + } + + /** + * Checks if there is a current element after calls to rewind() or next(). + */ + public function valid(): bool + { + return $this->position < \count($this->nodes); + } + + /** + * Returns the key of the current element. + */ + public function key(): int + { + return $this->position; + } + + /** + * Returns the current element. + */ + public function current(): AbstractNode + { + return $this->valid() ? $this->nodes[$this->position] : null; + } + + /** + * Moves forward to next element. + */ + public function next(): void + { + $this->position++; + } + + /** + * Returns the sub iterator for the current element. + * + * @return Iterator + */ + public function getChildren(): self + { + return new self($this->nodes[$this->position]); + } + + /** + * Checks whether the current element has children. + */ + public function hasChildren(): bool + { + return $this->nodes[$this->position] instanceof Directory; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Node; + +use SebastianBergmann\CodeCoverage\InvalidArgumentException; + +/** + * Represents a directory in the code coverage information tree. + */ +final class Directory extends AbstractNode implements \IteratorAggregate +{ + /** + * @var AbstractNode[] + */ + private $children = []; + + /** + * @var Directory[] + */ + private $directories = []; + + /** + * @var File[] + */ + private $files = []; + + /** + * @var array + */ + private $classes; + + /** + * @var array + */ + private $traits; + + /** + * @var array + */ + private $functions; + + /** + * @var array + */ + private $linesOfCode; + + /** + * @var int + */ + private $numFiles = -1; + + /** + * @var int + */ + private $numExecutableLines = -1; + + /** + * @var int + */ + private $numExecutedLines = -1; + + /** + * @var int + */ + private $numClasses = -1; + + /** + * @var int + */ + private $numTestedClasses = -1; + + /** + * @var int + */ + private $numTraits = -1; + + /** + * @var int + */ + private $numTestedTraits = -1; + + /** + * @var int + */ + private $numMethods = -1; + + /** + * @var int + */ + private $numTestedMethods = -1; + + /** + * @var int + */ + private $numFunctions = -1; + + /** + * @var int + */ + private $numTestedFunctions = -1; + + /** + * Returns the number of files in/under this node. + */ + public function count(): int + { + if ($this->numFiles === -1) { + $this->numFiles = 0; + + foreach ($this->children as $child) { + $this->numFiles += \count($child); + } + } + + return $this->numFiles; + } + + /** + * Returns an iterator for this node. + */ + public function getIterator(): \RecursiveIteratorIterator + { + return new \RecursiveIteratorIterator( + new Iterator($this), + \RecursiveIteratorIterator::SELF_FIRST + ); + } + + /** + * Adds a new directory. + */ + public function addDirectory(string $name): self + { + $directory = new self($name, $this); + + $this->children[] = $directory; + $this->directories[] = &$this->children[\count($this->children) - 1]; + + return $directory; + } + + /** + * Adds a new file. + * + * @throws InvalidArgumentException + */ + public function addFile(string $name, array $coverageData, array $testData, bool $cacheTokens): File + { + $file = new File($name, $this, $coverageData, $testData, $cacheTokens); + + $this->children[] = $file; + $this->files[] = &$this->children[\count($this->children) - 1]; + + $this->numExecutableLines = -1; + $this->numExecutedLines = -1; + + return $file; + } + + /** + * Returns the directories in this directory. + */ + public function getDirectories(): array + { + return $this->directories; + } + + /** + * Returns the files in this directory. + */ + public function getFiles(): array + { + return $this->files; + } + + /** + * Returns the child nodes of this node. + */ + public function getChildNodes(): array + { + return $this->children; + } + + /** + * Returns the classes of this node. + */ + public function getClasses(): array + { + if ($this->classes === null) { + $this->classes = []; + + foreach ($this->children as $child) { + $this->classes = \array_merge( + $this->classes, + $child->getClasses() + ); + } + } + + return $this->classes; + } + + /** + * Returns the traits of this node. + */ + public function getTraits(): array + { + if ($this->traits === null) { + $this->traits = []; + + foreach ($this->children as $child) { + $this->traits = \array_merge( + $this->traits, + $child->getTraits() + ); + } + } + + return $this->traits; + } + + /** + * Returns the functions of this node. + */ + public function getFunctions(): array + { + if ($this->functions === null) { + $this->functions = []; + + foreach ($this->children as $child) { + $this->functions = \array_merge( + $this->functions, + $child->getFunctions() + ); + } + } + + return $this->functions; + } + + /** + * Returns the LOC/CLOC/NCLOC of this node. + */ + public function getLinesOfCode(): array + { + if ($this->linesOfCode === null) { + $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; + + foreach ($this->children as $child) { + $linesOfCode = $child->getLinesOfCode(); + + $this->linesOfCode['loc'] += $linesOfCode['loc']; + $this->linesOfCode['cloc'] += $linesOfCode['cloc']; + $this->linesOfCode['ncloc'] += $linesOfCode['ncloc']; + } + } + + return $this->linesOfCode; + } + + /** + * Returns the number of executable lines. + */ + public function getNumExecutableLines(): int + { + if ($this->numExecutableLines === -1) { + $this->numExecutableLines = 0; + + foreach ($this->children as $child) { + $this->numExecutableLines += $child->getNumExecutableLines(); + } + } + + return $this->numExecutableLines; + } + + /** + * Returns the number of executed lines. + */ + public function getNumExecutedLines(): int + { + if ($this->numExecutedLines === -1) { + $this->numExecutedLines = 0; + + foreach ($this->children as $child) { + $this->numExecutedLines += $child->getNumExecutedLines(); + } + } + + return $this->numExecutedLines; + } + + /** + * Returns the number of classes. + */ + public function getNumClasses(): int + { + if ($this->numClasses === -1) { + $this->numClasses = 0; + + foreach ($this->children as $child) { + $this->numClasses += $child->getNumClasses(); + } + } + + return $this->numClasses; + } + + /** + * Returns the number of tested classes. + */ + public function getNumTestedClasses(): int + { + if ($this->numTestedClasses === -1) { + $this->numTestedClasses = 0; + + foreach ($this->children as $child) { + $this->numTestedClasses += $child->getNumTestedClasses(); + } + } + + return $this->numTestedClasses; + } + + /** + * Returns the number of traits. + */ + public function getNumTraits(): int + { + if ($this->numTraits === -1) { + $this->numTraits = 0; + + foreach ($this->children as $child) { + $this->numTraits += $child->getNumTraits(); + } + } + + return $this->numTraits; + } + + /** + * Returns the number of tested traits. + */ + public function getNumTestedTraits(): int + { + if ($this->numTestedTraits === -1) { + $this->numTestedTraits = 0; + + foreach ($this->children as $child) { + $this->numTestedTraits += $child->getNumTestedTraits(); + } + } + + return $this->numTestedTraits; + } + + /** + * Returns the number of methods. + */ + public function getNumMethods(): int + { + if ($this->numMethods === -1) { + $this->numMethods = 0; + + foreach ($this->children as $child) { + $this->numMethods += $child->getNumMethods(); + } + } + + return $this->numMethods; + } + + /** + * Returns the number of tested methods. + */ + public function getNumTestedMethods(): int + { + if ($this->numTestedMethods === -1) { + $this->numTestedMethods = 0; + + foreach ($this->children as $child) { + $this->numTestedMethods += $child->getNumTestedMethods(); + } + } + + return $this->numTestedMethods; + } + + /** + * Returns the number of functions. + */ + public function getNumFunctions(): int + { + if ($this->numFunctions === -1) { + $this->numFunctions = 0; + + foreach ($this->children as $child) { + $this->numFunctions += $child->getNumFunctions(); + } + } + + return $this->numFunctions; + } + + /** + * Returns the number of tested functions. + */ + public function getNumTestedFunctions(): int + { + if ($this->numTestedFunctions === -1) { + $this->numTestedFunctions = 0; + + foreach ($this->children as $child) { + $this->numTestedFunctions += $child->getNumTestedFunctions(); + } + } + + return $this->numTestedFunctions; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Node; + +use SebastianBergmann\CodeCoverage\CodeCoverage; + +final class Builder +{ + public function build(CodeCoverage $coverage): Directory + { + $files = $coverage->getData(); + $commonPath = $this->reducePaths($files); + $root = new Directory( + $commonPath, + null + ); + + $this->addItems( + $root, + $this->buildDirectoryStructure($files), + $coverage->getTests(), + $coverage->getCacheTokens() + ); + + return $root; + } + + private function addItems(Directory $root, array $items, array $tests, bool $cacheTokens): void + { + foreach ($items as $key => $value) { + if (\substr($key, -2) == '/f') { + $key = \substr($key, 0, -2); + + if (\file_exists($root->getPath() . \DIRECTORY_SEPARATOR . $key)) { + $root->addFile($key, $value, $tests, $cacheTokens); + } + } else { + $child = $root->addDirectory($key); + $this->addItems($child, $value, $tests, $cacheTokens); + } + } + } + + /** + * Builds an array representation of the directory structure. + * + * For instance, + * + * + * Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * is transformed into + * + * + * Array + * ( + * [.] => Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * ) + * + */ + private function buildDirectoryStructure(array $files): array + { + $result = []; + + foreach ($files as $path => $file) { + $path = \explode(\DIRECTORY_SEPARATOR, $path); + $pointer = &$result; + $max = \count($path); + + for ($i = 0; $i < $max; $i++) { + $type = ''; + + if ($i == ($max - 1)) { + $type = '/f'; + } + + $pointer = &$pointer[$path[$i] . $type]; + } + + $pointer = $file; + } + + return $result; + } + + /** + * Reduces the paths by cutting the longest common start path. + * + * For instance, + * + * + * Array + * ( + * [/home/sb/Money/Money.php] => Array + * ( + * ... + * ) + * + * [/home/sb/Money/MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + * + * is reduced to + * + * + * Array + * ( + * [Money.php] => Array + * ( + * ... + * ) + * + * [MoneyBag.php] => Array + * ( + * ... + * ) + * ) + * + */ + private function reducePaths(array &$files): string + { + if (empty($files)) { + return '.'; + } + + $commonPath = ''; + $paths = \array_keys($files); + + if (\count($files) === 1) { + $commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR; + $files[\basename($paths[0])] = $files[$paths[0]]; + + unset($files[$paths[0]]); + + return $commonPath; + } + + $max = \count($paths); + + for ($i = 0; $i < $max; $i++) { + // strip phar:// prefixes + if (\strpos($paths[$i], 'phar://') === 0) { + $paths[$i] = \substr($paths[$i], 7); + $paths[$i] = \str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]); + } + $paths[$i] = \explode(\DIRECTORY_SEPARATOR, $paths[$i]); + + if (empty($paths[$i][0])) { + $paths[$i][0] = \DIRECTORY_SEPARATOR; + } + } + + $done = false; + $max = \count($paths); + + while (!$done) { + for ($i = 0; $i < $max - 1; $i++) { + if (!isset($paths[$i][0]) || + !isset($paths[$i + 1][0]) || + $paths[$i][0] != $paths[$i + 1][0]) { + $done = true; + + break; + } + } + + if (!$done) { + $commonPath .= $paths[0][0]; + + if ($paths[0][0] != \DIRECTORY_SEPARATOR) { + $commonPath .= \DIRECTORY_SEPARATOR; + } + + for ($i = 0; $i < $max; $i++) { + \array_shift($paths[$i]); + } + } + } + + $original = \array_keys($files); + $max = \count($original); + + for ($i = 0; $i < $max; $i++) { + $files[\implode(\DIRECTORY_SEPARATOR, $paths[$i])] = $files[$original[$i]]; + unset($files[$original[$i]]); + } + + \ksort($files); + + return \substr($commonPath, 0, -1); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Node; + +/** + * Represents a file in the code coverage information tree. + */ +final class File extends AbstractNode +{ + /** + * @var array + */ + private $coverageData; + + /** + * @var array + */ + private $testData; + + /** + * @var int + */ + private $numExecutableLines = 0; + + /** + * @var int + */ + private $numExecutedLines = 0; + + /** + * @var array + */ + private $classes = []; + + /** + * @var array + */ + private $traits = []; + + /** + * @var array + */ + private $functions = []; + + /** + * @var array + */ + private $linesOfCode = []; + + /** + * @var int + */ + private $numClasses; + + /** + * @var int + */ + private $numTestedClasses = 0; + + /** + * @var int + */ + private $numTraits; + + /** + * @var int + */ + private $numTestedTraits = 0; + + /** + * @var int + */ + private $numMethods; + + /** + * @var int + */ + private $numTestedMethods; + + /** + * @var int + */ + private $numTestedFunctions; + + /** + * @var bool + */ + private $cacheTokens; + + /** + * @var array + */ + private $codeUnitsByLine = []; + + public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens) + { + parent::__construct($name, $parent); + + $this->coverageData = $coverageData; + $this->testData = $testData; + $this->cacheTokens = $cacheTokens; + + $this->calculateStatistics(); + } + + /** + * Returns the number of files in/under this node. + */ + public function count(): int + { + return 1; + } + + /** + * Returns the code coverage data of this node. + */ + public function getCoverageData(): array + { + return $this->coverageData; + } + + /** + * Returns the test data of this node. + */ + public function getTestData(): array + { + return $this->testData; + } + + /** + * Returns the classes of this node. + */ + public function getClasses(): array + { + return $this->classes; + } + + /** + * Returns the traits of this node. + */ + public function getTraits(): array + { + return $this->traits; + } + + /** + * Returns the functions of this node. + */ + public function getFunctions(): array + { + return $this->functions; + } + + /** + * Returns the LOC/CLOC/NCLOC of this node. + */ + public function getLinesOfCode(): array + { + return $this->linesOfCode; + } + + /** + * Returns the number of executable lines. + */ + public function getNumExecutableLines(): int + { + return $this->numExecutableLines; + } + + /** + * Returns the number of executed lines. + */ + public function getNumExecutedLines(): int + { + return $this->numExecutedLines; + } + + /** + * Returns the number of classes. + */ + public function getNumClasses(): int + { + if ($this->numClasses === null) { + $this->numClasses = 0; + + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numClasses++; + + continue 2; + } + } + } + } + + return $this->numClasses; + } + + /** + * Returns the number of tested classes. + */ + public function getNumTestedClasses(): int + { + return $this->numTestedClasses; + } + + /** + * Returns the number of traits. + */ + public function getNumTraits(): int + { + if ($this->numTraits === null) { + $this->numTraits = 0; + + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numTraits++; + + continue 2; + } + } + } + } + + return $this->numTraits; + } + + /** + * Returns the number of tested traits. + */ + public function getNumTestedTraits(): int + { + return $this->numTestedTraits; + } + + /** + * Returns the number of methods. + */ + public function getNumMethods(): int + { + if ($this->numMethods === null) { + $this->numMethods = 0; + + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numMethods++; + } + } + } + + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0) { + $this->numMethods++; + } + } + } + } + + return $this->numMethods; + } + + /** + * Returns the number of tested methods. + */ + public function getNumTestedMethods(): int + { + if ($this->numTestedMethods === null) { + $this->numTestedMethods = 0; + + foreach ($this->classes as $class) { + foreach ($class['methods'] as $method) { + if ($method['executableLines'] > 0 && + $method['coverage'] === 100) { + $this->numTestedMethods++; + } + } + } + + foreach ($this->traits as $trait) { + foreach ($trait['methods'] as $method) { + if ($method['executableLines'] > 0 && + $method['coverage'] === 100) { + $this->numTestedMethods++; + } + } + } + } + + return $this->numTestedMethods; + } + + /** + * Returns the number of functions. + */ + public function getNumFunctions(): int + { + return \count($this->functions); + } + + /** + * Returns the number of tested functions. + */ + public function getNumTestedFunctions(): int + { + if ($this->numTestedFunctions === null) { + $this->numTestedFunctions = 0; + + foreach ($this->functions as $function) { + if ($function['executableLines'] > 0 && + $function['coverage'] === 100) { + $this->numTestedFunctions++; + } + } + } + + return $this->numTestedFunctions; + } + + private function calculateStatistics(): void + { + if ($this->cacheTokens) { + $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); + } else { + $tokens = new \PHP_Token_Stream($this->getPath()); + } + + $this->linesOfCode = $tokens->getLinesOfCode(); + + foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = []; + } + + try { + $this->processClasses($tokens); + $this->processTraits($tokens); + $this->processFunctions($tokens); + } catch (\OutOfBoundsException $e) { + // This can happen with PHP_Token_Stream if the file is syntactically invalid, + // and probably affects a file that wasn't executed. + } + unset($tokens); + + foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { + if (isset($this->coverageData[$lineNumber])) { + foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { + $codeUnit['executableLines']++; + } + + unset($codeUnit); + + $this->numExecutableLines++; + + if (\count($this->coverageData[$lineNumber]) > 0) { + foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { + $codeUnit['executedLines']++; + } + + unset($codeUnit); + + $this->numExecutedLines++; + } + } + } + + foreach ($this->traits as &$trait) { + foreach ($trait['methods'] as &$method) { + if ($method['executableLines'] > 0) { + $method['coverage'] = ($method['executedLines'] / + $method['executableLines']) * 100; + } else { + $method['coverage'] = 100; + } + + $method['crap'] = $this->crap( + $method['ccn'], + $method['coverage'] + ); + + $trait['ccn'] += $method['ccn']; + } + + unset($method); + + if ($trait['executableLines'] > 0) { + $trait['coverage'] = ($trait['executedLines'] / + $trait['executableLines']) * 100; + + if ($trait['coverage'] === 100) { + $this->numTestedClasses++; + } + } else { + $trait['coverage'] = 100; + } + + $trait['crap'] = $this->crap( + $trait['ccn'], + $trait['coverage'] + ); + } + + unset($trait); + + foreach ($this->classes as &$class) { + foreach ($class['methods'] as &$method) { + if ($method['executableLines'] > 0) { + $method['coverage'] = ($method['executedLines'] / + $method['executableLines']) * 100; + } else { + $method['coverage'] = 100; + } + + $method['crap'] = $this->crap( + $method['ccn'], + $method['coverage'] + ); + + $class['ccn'] += $method['ccn']; + } + + unset($method); + + if ($class['executableLines'] > 0) { + $class['coverage'] = ($class['executedLines'] / + $class['executableLines']) * 100; + + if ($class['coverage'] === 100) { + $this->numTestedClasses++; + } + } else { + $class['coverage'] = 100; + } + + $class['crap'] = $this->crap( + $class['ccn'], + $class['coverage'] + ); + } + + unset($class); + + foreach ($this->functions as &$function) { + if ($function['executableLines'] > 0) { + $function['coverage'] = ($function['executedLines'] / + $function['executableLines']) * 100; + } else { + $function['coverage'] = 100; + } + + if ($function['coverage'] === 100) { + $this->numTestedFunctions++; + } + + $function['crap'] = $this->crap( + $function['ccn'], + $function['coverage'] + ); + } + } + + private function processClasses(\PHP_Token_Stream $tokens): void + { + $classes = $tokens->getClasses(); + $link = $this->getId() . '.html#'; + + foreach ($classes as $className => $class) { + if (\strpos($className, 'anonymous') === 0) { + continue; + } + + if (!empty($class['package']['namespace'])) { + $className = $class['package']['namespace'] . '\\' . $className; + } + + $this->classes[$className] = [ + 'className' => $className, + 'methods' => [], + 'startLine' => $class['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => 0, + 'coverage' => 0, + 'crap' => 0, + 'package' => $class['package'], + 'link' => $link . $class['startLine'], + ]; + + foreach ($class['methods'] as $methodName => $method) { + if (\strpos($methodName, 'anonymous') === 0) { + continue; + } + + $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); + + foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = [ + &$this->classes[$className], + &$this->classes[$className]['methods'][$methodName], + ]; + } + } + } + } + + private function processTraits(\PHP_Token_Stream $tokens): void + { + $traits = $tokens->getTraits(); + $link = $this->getId() . '.html#'; + + foreach ($traits as $traitName => $trait) { + $this->traits[$traitName] = [ + 'traitName' => $traitName, + 'methods' => [], + 'startLine' => $trait['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => 0, + 'coverage' => 0, + 'crap' => 0, + 'package' => $trait['package'], + 'link' => $link . $trait['startLine'], + ]; + + foreach ($trait['methods'] as $methodName => $method) { + if (\strpos($methodName, 'anonymous') === 0) { + continue; + } + + $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); + + foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = [ + &$this->traits[$traitName], + &$this->traits[$traitName]['methods'][$methodName], + ]; + } + } + } + } + + private function processFunctions(\PHP_Token_Stream $tokens): void + { + $functions = $tokens->getFunctions(); + $link = $this->getId() . '.html#'; + + foreach ($functions as $functionName => $function) { + if (\strpos($functionName, 'anonymous') === 0) { + continue; + } + + $this->functions[$functionName] = [ + 'functionName' => $functionName, + 'signature' => $function['signature'], + 'startLine' => $function['startLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => $function['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $function['startLine'], + ]; + + foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) { + $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; + } + } + } + + private function crap(int $ccn, float $coverage): string + { + if ($coverage === 0) { + return (string) ($ccn ** 2 + $ccn); + } + + if ($coverage >= 95) { + return (string) $ccn; + } + + return \sprintf( + '%01.2F', + $ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn + ); + } + + private function newMethod(string $methodName, array $method, string $link): array + { + return [ + 'methodName' => $methodName, + 'visibility' => $method['visibility'], + 'signature' => $method['signature'], + 'startLine' => $method['startLine'], + 'endLine' => $method['endLine'], + 'executableLines' => 0, + 'executedLines' => 0, + 'ccn' => $method['ccn'], + 'coverage' => 0, + 'crap' => 0, + 'link' => $link . $method['startLine'], + ]; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage\Node; + +use SebastianBergmann\CodeCoverage\Util; + +/** + * Base class for nodes in the code coverage information tree. + */ +abstract class AbstractNode implements \Countable +{ + /** + * @var string + */ + private $name; + + /** + * @var string + */ + private $path; + + /** + * @var array + */ + private $pathArray; + + /** + * @var AbstractNode + */ + private $parent; + + /** + * @var string + */ + private $id; + + public function __construct(string $name, self $parent = null) + { + if (\substr($name, -1) == \DIRECTORY_SEPARATOR) { + $name = \substr($name, 0, -1); + } + + $this->name = $name; + $this->parent = $parent; + } + + public function getName(): string + { + return $this->name; + } + + public function getId(): string + { + if ($this->id === null) { + $parent = $this->getParent(); + + if ($parent === null) { + $this->id = 'index'; + } else { + $parentId = $parent->getId(); + + if ($parentId === 'index') { + $this->id = \str_replace(':', '_', $this->name); + } else { + $this->id = $parentId . '/' . $this->name; + } + } + } + + return $this->id; + } + + public function getPath(): string + { + if ($this->path === null) { + if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) { + $this->path = $this->name; + } else { + $this->path = $this->parent->getPath() . \DIRECTORY_SEPARATOR . $this->name; + } + } + + return $this->path; + } + + public function getPathAsArray(): array + { + if ($this->pathArray === null) { + if ($this->parent === null) { + $this->pathArray = []; + } else { + $this->pathArray = $this->parent->getPathAsArray(); + } + + $this->pathArray[] = $this; + } + + return $this->pathArray; + } + + public function getParent(): ?self + { + return $this->parent; + } + + /** + * Returns the percentage of classes that has been tested. + * + * @return int|string + */ + public function getTestedClassesPercent(bool $asString = true) + { + return Util::percent( + $this->getNumTestedClasses(), + $this->getNumClasses(), + $asString + ); + } + + /** + * Returns the percentage of traits that has been tested. + * + * @return int|string + */ + public function getTestedTraitsPercent(bool $asString = true) + { + return Util::percent( + $this->getNumTestedTraits(), + $this->getNumTraits(), + $asString + ); + } + + /** + * Returns the percentage of classes and traits that has been tested. + * + * @return int|string + */ + public function getTestedClassesAndTraitsPercent(bool $asString = true) + { + return Util::percent( + $this->getNumTestedClassesAndTraits(), + $this->getNumClassesAndTraits(), + $asString + ); + } + + /** + * Returns the percentage of functions that has been tested. + * + * @return int|string + */ + public function getTestedFunctionsPercent(bool $asString = true) + { + return Util::percent( + $this->getNumTestedFunctions(), + $this->getNumFunctions(), + $asString + ); + } + + /** + * Returns the percentage of methods that has been tested. + * + * @return int|string + */ + public function getTestedMethodsPercent(bool $asString = true) + { + return Util::percent( + $this->getNumTestedMethods(), + $this->getNumMethods(), + $asString + ); + } + + /** + * Returns the percentage of functions and methods that has been tested. + * + * @return int|string + */ + public function getTestedFunctionsAndMethodsPercent(bool $asString = true) + { + return Util::percent( + $this->getNumTestedFunctionsAndMethods(), + $this->getNumFunctionsAndMethods(), + $asString + ); + } + + /** + * Returns the percentage of executed lines. + * + * @return int|string + */ + public function getLineExecutedPercent(bool $asString = true) + { + return Util::percent( + $this->getNumExecutedLines(), + $this->getNumExecutableLines(), + $asString + ); + } + + /** + * Returns the number of classes and traits. + */ + public function getNumClassesAndTraits(): int + { + return $this->getNumClasses() + $this->getNumTraits(); + } + + /** + * Returns the number of tested classes and traits. + */ + public function getNumTestedClassesAndTraits(): int + { + return $this->getNumTestedClasses() + $this->getNumTestedTraits(); + } + + /** + * Returns the classes and traits of this node. + */ + public function getClassesAndTraits(): array + { + return \array_merge($this->getClasses(), $this->getTraits()); + } + + /** + * Returns the number of functions and methods. + */ + public function getNumFunctionsAndMethods(): int + { + return $this->getNumFunctions() + $this->getNumMethods(); + } + + /** + * Returns the number of tested functions and methods. + */ + public function getNumTestedFunctionsAndMethods(): int + { + return $this->getNumTestedFunctions() + $this->getNumTestedMethods(); + } + + /** + * Returns the functions and methods of this node. + */ + public function getFunctionsAndMethods(): array + { + return \array_merge($this->getFunctions(), $this->getMethods()); + } + + /** + * Returns the classes of this node. + */ + abstract public function getClasses(): array; + + /** + * Returns the traits of this node. + */ + abstract public function getTraits(): array; + + /** + * Returns the functions of this node. + */ + abstract public function getFunctions(): array; + + /** + * Returns the LOC/CLOC/NCLOC of this node. + */ + abstract public function getLinesOfCode(): array; + + /** + * Returns the number of executable lines. + */ + abstract public function getNumExecutableLines(): int; + + /** + * Returns the number of executed lines. + */ + abstract public function getNumExecutedLines(): int; + + /** + * Returns the number of classes. + */ + abstract public function getNumClasses(): int; + + /** + * Returns the number of tested classes. + */ + abstract public function getNumTestedClasses(): int; + + /** + * Returns the number of traits. + */ + abstract public function getNumTraits(): int; + + /** + * Returns the number of tested traits. + */ + abstract public function getNumTestedTraits(): int; + + /** + * Returns the number of methods. + */ + abstract public function getNumMethods(): int; + + /** + * Returns the number of tested methods. + */ + abstract public function getNumTestedMethods(): int; + + /** + * Returns the number of functions. + */ + abstract public function getNumFunctions(): int; + + /** + * Returns the number of tested functions. + */ + abstract public function getNumTestedFunctions(): int; +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\CodeCoverage; + +use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; + +/** + * Filter for whitelisting of code coverage information. + */ +final class Filter +{ + /** + * Source files that are whitelisted. + * + * @var array + */ + private $whitelistedFiles = []; + + /** + * Remembers the result of the `is_file()` calls. + * + * @var bool[] + */ + private $isFileCallsCache = []; + + /** + * Adds a directory to the whitelist (recursively). + */ + public function addDirectoryToWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void + { + $facade = new FileIteratorFacade; + $files = $facade->getFilesAsArray($directory, $suffix, $prefix); + + foreach ($files as $file) { + $this->addFileToWhitelist($file); + } + } + + /** + * Adds a file to the whitelist. + */ + public function addFileToWhitelist(string $filename): void + { + $this->whitelistedFiles[\realpath($filename)] = true; + } + + /** + * Adds files to the whitelist. + * + * @param string[] $files + */ + public function addFilesToWhitelist(array $files): void + { + foreach ($files as $file) { + $this->addFileToWhitelist($file); + } + } + + /** + * Removes a directory from the whitelist (recursively). + */ + public function removeDirectoryFromWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void + { + $facade = new FileIteratorFacade; + $files = $facade->getFilesAsArray($directory, $suffix, $prefix); + + foreach ($files as $file) { + $this->removeFileFromWhitelist($file); + } + } + + /** + * Removes a file from the whitelist. + */ + public function removeFileFromWhitelist(string $filename): void + { + $filename = \realpath($filename); + + unset($this->whitelistedFiles[$filename]); + } + + /** + * Checks whether a filename is a real filename. + */ + public function isFile(string $filename): bool + { + if (isset($this->isFileCallsCache[$filename])) { + return $this->isFileCallsCache[$filename]; + } + + if ($filename === '-' || + \strpos($filename, 'vfs://') === 0 || + \strpos($filename, 'xdebug://debug-eval') !== false || + \strpos($filename, 'eval()\'d code') !== false || + \strpos($filename, 'runtime-created function') !== false || + \strpos($filename, 'runkit created function') !== false || + \strpos($filename, 'assert code') !== false || + \strpos($filename, 'regexp code') !== false || + \strpos($filename, 'Standard input code') !== false) { + $isFile = false; + } else { + $isFile = \file_exists($filename); + } + + $this->isFileCallsCache[$filename] = $isFile; + + return $isFile; + } + + /** + * Checks whether or not a file is filtered. + */ + public function isFiltered(string $filename): bool + { + if (!$this->isFile($filename)) { + return true; + } + + return !isset($this->whitelistedFiles[$filename]); + } + + /** + * Returns the list of whitelisted files. + * + * @return string[] + */ + public function getWhitelist(): array + { + return \array_keys($this->whitelistedFiles); + } + + /** + * Returns whether this filter has a whitelist. + */ + public function hasWhitelist(): bool + { + return !empty($this->whitelistedFiles); + } + + /** + * Returns the whitelisted files. + * + * @return string[] + */ + public function getWhitelistedFiles(): array + { + return $this->whitelistedFiles; + } + + /** + * Sets the whitelisted files. + */ + public function setWhitelistedFiles(array $whitelistedFiles): void + { + $this->whitelistedFiles = $whitelistedFiles; + } +} +php-code-coverage + +Copyright (c) 2009-2018, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +Exporter + +Copyright (c) 2002-2017, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Exporter; + +use SebastianBergmann\RecursionContext\Context; + +/** + * A nifty utility for visualizing PHP variables. + * + * + * export(new Exception); + * + */ +class Exporter +{ + /** + * Exports a value as a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value + * @param int $indentation The indentation level of the 2nd+ line + * + * @return string + */ + public function export($value, $indentation = 0) + { + return $this->recursiveExport($value, $indentation); + } + + /** + * @param mixed $data + * @param Context $context + * + * @return string + */ + public function shortenedRecursiveExport(&$data, Context $context = null) + { + $result = []; + $exporter = new self(); + + if (!$context) { + $context = new Context; + } + + $array = $data; + $context->add($data); + + foreach ($array as $key => $value) { + if (is_array($value)) { + if ($context->contains($data[$key]) !== false) { + $result[] = '*RECURSION*'; + } else { + $result[] = sprintf( + 'array(%s)', + $this->shortenedRecursiveExport($data[$key], $context) + ); + } + } else { + $result[] = $exporter->shortenedExport($value); + } + } + + return implode(', ', $result); + } + + /** + * Exports a value into a single-line string + * + * The output of this method is similar to the output of + * SebastianBergmann\Exporter\Exporter::export(). + * + * Newlines are replaced by the visible string '\n'. + * Contents of arrays and objects (if any) are replaced by '...'. + * + * @param mixed $value + * + * @return string + * + * @see SebastianBergmann\Exporter\Exporter::export + */ + public function shortenedExport($value) + { + if (is_string($value)) { + $string = str_replace("\n", '', $this->export($value)); + + if (function_exists('mb_strlen')) { + if (mb_strlen($string) > 40) { + $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); + } + } else { + if (strlen($string) > 40) { + $string = substr($string, 0, 30) . '...' . substr($string, -7); + } + } + + return $string; + } + + if (is_object($value)) { + return sprintf( + '%s Object (%s)', + get_class($value), + count($this->toArray($value)) > 0 ? '...' : '' + ); + } + + if (is_array($value)) { + return sprintf( + 'Array (%s)', + count($value) > 0 ? '...' : '' + ); + } + + return $this->export($value); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param mixed $value + * + * @return array + */ + public function toArray($value) + { + if (!is_object($value)) { + return (array) $value; + } + + $array = []; + + foreach ((array) $value as $key => $val) { + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + + $array[$key] = $val; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof \SplObjectStorage) { + // However, the fast method does work in HHVM, and exposes the + // internal implementation. Hide it again. + if (property_exists('\SplObjectStorage', '__storage')) { + unset($array['__storage']); + } elseif (property_exists('\SplObjectStorage', 'storage')) { + unset($array['storage']); + } + + if (property_exists('\SplObjectStorage', '__key')) { + unset($array['__key']); + } + + foreach ($value as $key => $val) { + $array[spl_object_hash($val)] = [ + 'obj' => $val, + 'inf' => $value->getInfo(), + ]; + } + } + + return $array; + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * + * @return string + * + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected function recursiveExport(&$value, $indentation, $processed = null) + { + if ($value === null) { + return 'null'; + } + + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + if (is_float($value) && floatval(intval($value)) === $value) { + return "$value.0"; + } + + if (is_resource($value)) { + return sprintf( + 'resource(%d) of type (%s)', + $value, + get_resource_type($value) + ); + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace('', "\n", + str_replace( + ["\r\n", "\n\r", "\r", "\n"], + ['\r\n', '\n\r', '\r', '\n'], + $value + ) + ) . + "'"; + } + + $whitespace = str_repeat(' ', 4 * $indentation); + + if (!$processed) { + $processed = new Context; + } + + if (is_array($value)) { + if (($key = $processed->contains($value)) !== false) { + return 'Array &' . $key; + } + + $array = $value; + $key = $processed->add($value); + $values = ''; + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + $this->recursiveExport($k, $indentation), + $this->recursiveExport($value[$k], $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('Array &%s (%s)', $key, $values); + } + + if (is_object($value)) { + $class = get_class($value); + + if ($hash = $processed->contains($value)) { + return sprintf('%s Object &%s', $class, $hash); + } + + $hash = $processed->add($value); + $values = ''; + $array = $this->toArray($value); + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + $this->recursiveExport($k, $indentation), + $this->recursiveExport($v, $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('%s Object &%s (%s)', $class, $hash, $values); + } + + return var_export($value, true); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class VersionNumber { + /** + * @var int + */ + private $value; + + /** + * @param mixed $value + */ + public function __construct($value) { + if (is_numeric($value)) { + $this->value = $value; + } + } + + /** + * @return bool + */ + public function isAny() { + return $this->value === null; + } + + /** + * @return int + */ + public function getValue() { + return $this->value; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class Version { + /** + * @var VersionNumber + */ + private $major; + + /** + * @var VersionNumber + */ + private $minor; + + /** + * @var VersionNumber + */ + private $patch; + + /** + * @var PreReleaseSuffix + */ + private $preReleaseSuffix; + + /** + * @var string + */ + private $versionString = ''; + + /** + * @param string $versionString + */ + public function __construct($versionString) { + $this->ensureVersionStringIsValid($versionString); + + $this->versionString = $versionString; + } + + /** + * @return PreReleaseSuffix + */ + public function getPreReleaseSuffix() { + return $this->preReleaseSuffix; + } + + /** + * @return string + */ + public function getVersionString() { + return $this->versionString; + } + + /** + * @return bool + */ + public function hasPreReleaseSuffix() { + return $this->preReleaseSuffix !== null; + } + + /** + * @param Version $version + * + * @return bool + */ + public function isGreaterThan(Version $version) { + if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { + return false; + } + + if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) { + return true; + } + + if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) { + return false; + } + + if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) { + return true; + } + + if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) { + return false; + } + + if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) { + return true; + } + + if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { + return false; + } + + if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { + return true; + } + + if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) { + return false; + } + + return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); + } + + /** + * @return VersionNumber + */ + public function getMajor() { + return $this->major; + } + + /** + * @return VersionNumber + */ + public function getMinor() { + return $this->minor; + } + + /** + * @return VersionNumber + */ + public function getPatch() { + return $this->patch; + } + + /** + * @param array $matches + */ + private function parseVersion(array $matches) { + $this->major = new VersionNumber($matches['Major']); + $this->minor = new VersionNumber($matches['Minor']); + $this->patch = isset($matches['Patch']) ? new VersionNumber($matches['Patch']) : new VersionNumber(null); + + if (isset($matches['PreReleaseSuffix'])) { + $this->preReleaseSuffix = new PreReleaseSuffix($matches['PreReleaseSuffix']); + } + } + + /** + * @param string $version + * + * @throws InvalidVersionException + */ + private function ensureVersionStringIsValid($version) { + $regex = '/^v? + (?(0|(?:[1-9][0-9]*))) + \\. + (?(0|(?:[1-9][0-9]*))) + (\\. + (?(0|(?:[1-9][0-9]*))) + )? + (?: + - + (?(?:(dev|beta|b|RC|alpha|a|patch|p)\.?\d*)) + )? + $/x'; + + if (preg_match($regex, $version, $matches) !== 1) { + throw new InvalidVersionException( + sprintf("Version string '%s' does not follow SemVer semantics", $version) + ); + } + + $this->parseVersion($matches); + } +} +versionString = $versionString; + + $this->parseVersion($versionString); + } + + /** + * @return string + */ + public function getLabel() { + return $this->label; + } + + /** + * @return string + */ + public function getBuildMetaData() { + return $this->buildMetaData; + } + + /** + * @return string + */ + public function getVersionString() { + return $this->versionString; + } + + /** + * @return VersionNumber + */ + public function getMajor() { + return $this->major; + } + + /** + * @return VersionNumber + */ + public function getMinor() { + return $this->minor; + } + + /** + * @return VersionNumber + */ + public function getPatch() { + return $this->patch; + } + + /** + * @param $versionString + */ + private function parseVersion($versionString) { + $this->extractBuildMetaData($versionString); + $this->extractLabel($versionString); + + $versionSegments = explode('.', $versionString); + $this->major = new VersionNumber($versionSegments[0]); + + $minorValue = isset($versionSegments[1]) ? $versionSegments[1] : null; + $patchValue = isset($versionSegments[2]) ? $versionSegments[2] : null; + + $this->minor = new VersionNumber($minorValue); + $this->patch = new VersionNumber($patchValue); + } + + /** + * @param string $versionString + */ + private function extractBuildMetaData(&$versionString) { + if (preg_match('/\+(.*)/', $versionString, $matches) == 1) { + $this->buildMetaData = $matches[1]; + $versionString = str_replace($matches[0], '', $versionString); + } + } + + /** + * @param string $versionString + */ + private function extractLabel(&$versionString) { + if (preg_match('/\-(.*)/', $versionString, $matches) == 1) { + $this->label = $matches[1]; + $versionString = str_replace($matches[0], '', $versionString); + } + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class SpecificMajorVersionConstraint extends AbstractVersionConstraint { + /** + * @var int + */ + private $major = 0; + + /** + * @param string $originalValue + * @param int $major + */ + public function __construct($originalValue, $major) { + parent::__construct($originalValue); + + $this->major = $major; + } + + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + return $version->getMajor()->getValue() == $this->major; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class OrVersionConstraintGroup extends AbstractVersionConstraint { + /** + * @var VersionConstraint[] + */ + private $constraints = []; + + /** + * @param string $originalValue + * @param VersionConstraint[] $constraints + */ + public function __construct($originalValue, array $constraints) { + parent::__construct($originalValue); + + $this->constraints = $constraints; + } + + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + foreach ($this->constraints as $constraint) { + if ($constraint->complies($version)) { + return true; + } + } + + return false; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class SpecificMajorAndMinorVersionConstraint extends AbstractVersionConstraint { + /** + * @var int + */ + private $major = 0; + + /** + * @var int + */ + private $minor = 0; + + /** + * @param string $originalValue + * @param int $major + * @param int $minor + */ + public function __construct($originalValue, $major, $minor) { + parent::__construct($originalValue); + + $this->major = $major; + $this->minor = $minor; + } + + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + if ($version->getMajor()->getValue() != $this->major) { + return false; + } + + return $version->getMinor()->getValue() == $this->minor; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class AndVersionConstraintGroup extends AbstractVersionConstraint { + /** + * @var VersionConstraint[] + */ + private $constraints = []; + + /** + * @param string $originalValue + * @param VersionConstraint[] $constraints + */ + public function __construct($originalValue, array $constraints) { + parent::__construct($originalValue); + + $this->constraints = $constraints; + } + + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + foreach ($this->constraints as $constraint) { + if (!$constraint->complies($version)) { + return false; + } + } + + return true; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +abstract class AbstractVersionConstraint implements VersionConstraint { + /** + * @var string + */ + private $originalValue = ''; + + /** + * @param string $originalValue + */ + public function __construct($originalValue) { + $this->originalValue = $originalValue; + } + + /** + * @return string + */ + public function asString() { + return $this->originalValue; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class ExactVersionConstraint extends AbstractVersionConstraint { + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + return $this->asString() == $version->getVersionString(); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +interface VersionConstraint { + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version); + + /** + * @return string + */ + public function asString(); + +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class GreaterThanOrEqualToVersionConstraint extends AbstractVersionConstraint { + /** + * @var Version + */ + private $minimalVersion; + + /** + * @param string $originalValue + * @param Version $minimalVersion + */ + public function __construct($originalValue, Version $minimalVersion) { + parent::__construct($originalValue); + + $this->minimalVersion = $minimalVersion; + } + + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + return $version->getVersionString() == $this->minimalVersion->getVersionString() + || $version->isGreaterThan($this->minimalVersion); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class AnyVersionConstraint implements VersionConstraint { + /** + * @param Version $version + * + * @return bool + */ + public function complies(Version $version) { + return true; + } + + /** + * @return string + */ + public function asString() { + return '*'; + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +class VersionConstraintParser { + /** + * @param string $value + * + * @return VersionConstraint + * + * @throws UnsupportedVersionConstraintException + */ + public function parse($value) { + + if (strpos($value, '||') !== false) { + return $this->handleOrGroup($value); + } + + if (!preg_match('/^[\^~\*]?[\d.\*]+(?:-.*)?$/', $value)) { + throw new UnsupportedVersionConstraintException( + sprintf('Version constraint %s is not supported.', $value) + ); + } + + switch ($value[0]) { + case '~': + return $this->handleTildeOperator($value); + case '^': + return $this->handleCaretOperator($value); + } + + $version = new VersionConstraintValue($value); + + if ($version->getMajor()->isAny()) { + return new AnyVersionConstraint(); + } + + if ($version->getMinor()->isAny()) { + return new SpecificMajorVersionConstraint( + $version->getVersionString(), + $version->getMajor()->getValue() + ); + } + + if ($version->getPatch()->isAny()) { + return new SpecificMajorAndMinorVersionConstraint( + $version->getVersionString(), + $version->getMajor()->getValue(), + $version->getMinor()->getValue() + ); + } + + return new ExactVersionConstraint($version->getVersionString()); + } + + /** + * @param $value + * + * @return OrVersionConstraintGroup + */ + private function handleOrGroup($value) { + $constraints = []; + + foreach (explode('||', $value) as $groupSegment) { + $constraints[] = $this->parse(trim($groupSegment)); + } + + return new OrVersionConstraintGroup($value, $constraints); + } + + /** + * @param string $value + * + * @return AndVersionConstraintGroup + */ + private function handleTildeOperator($value) { + $version = new Version(substr($value, 1)); + $constraints = [ + new GreaterThanOrEqualToVersionConstraint($value, $version) + ]; + + if ($version->getPatch()->isAny()) { + $constraints[] = new SpecificMajorVersionConstraint( + $value, + $version->getMajor()->getValue() + ); + } else { + $constraints[] = new SpecificMajorAndMinorVersionConstraint( + $value, + $version->getMajor()->getValue(), + $version->getMinor()->getValue() + ); + } + + return new AndVersionConstraintGroup($value, $constraints); + } + + /** + * @param string $value + * + * @return AndVersionConstraintGroup + */ + private function handleCaretOperator($value) { + $version = new Version(substr($value, 1)); + + return new AndVersionConstraintGroup( + $value, + [ + new GreaterThanOrEqualToVersionConstraint($value, $version), + new SpecificMajorVersionConstraint($value, $version->getMajor()->getValue()) + ] + ); + } +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +interface Exception { +} +, Sebastian Heuer , Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PharIo\Version; + +final class UnsupportedVersionConstraintException extends \RuntimeException implements Exception { +} +phar-io/version + +Copyright (c) 2016-2017 Arne Blankerts , Sebastian Heuer and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Arne Blankerts nor the names of contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + 0, + 'a' => 1, + 'alpha' => 1, + 'b' => 2, + 'beta' => 2, + 'rc' => 3, + 'p' => 4, + 'patch' => 4, + ]; + + /** + * @var string + */ + private $value; + + /** + * @var int + */ + private $valueScore; + + /** + * @var int + */ + private $number = 0; + + /** + * @param string $value + */ + public function __construct($value) { + $this->parseValue($value); + } + + /** + * @return string + */ + public function getValue() { + return $this->value; + } + + /** + * @return int|null + */ + public function getNumber() { + return $this->number; + } + + /** + * @param PreReleaseSuffix $suffix + * + * @return bool + */ + public function isGreaterThan(PreReleaseSuffix $suffix) { + if ($this->valueScore > $suffix->valueScore) { + return true; + } + + if ($this->valueScore < $suffix->valueScore) { + return false; + } + + return $this->getNumber() > $suffix->getNumber(); + } + + /** + * @param $value + * + * @return int + */ + private function mapValueToScore($value) { + if (array_key_exists($value, $this->valueScoreMap)) { + return $this->valueScoreMap[$value]; + } + + return 0; + } + + private function parseValue($value) { + $regex = '/-?(dev|beta|b|rc|alpha|a|patch|p)\.?(\d*).*$/i'; + if (preg_match($regex, $value, $matches) !== 1) { + throw new InvalidPreReleaseSuffixException(sprintf('Invalid label %s', $value)); + } + + $this->value = $matches[1]; + if (isset($matches[2])) { + $this->number = (int)$matches[2]; + } + $this->valueScore = $this->mapValueToScore($this->value); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace SebastianBergmann\ResourceOperations; + +final class ResourceOperations +{ + /** + * @return string[] + */ + public static function getFunctions(): array + { + return [ + 'Directory::close', + 'Directory::read', + 'Directory::rewind', + 'DirectoryIterator::openFile', + 'FilesystemIterator::openFile', + 'Gmagick::readimagefile', + 'HttpResponse::getRequestBodyStream', + 'HttpResponse::getStream', + 'HttpResponse::setStream', + 'Imagick::pingImageFile', + 'Imagick::readImageFile', + 'Imagick::writeImageFile', + 'Imagick::writeImagesFile', + 'MongoGridFSCursor::__construct', + 'MongoGridFSFile::getResource', + 'MysqlndUhConnection::stmtInit', + 'MysqlndUhConnection::storeResult', + 'MysqlndUhConnection::useResult', + 'PDF_activate_item', + 'PDF_add_launchlink', + 'PDF_add_locallink', + 'PDF_add_nameddest', + 'PDF_add_note', + 'PDF_add_pdflink', + 'PDF_add_table_cell', + 'PDF_add_textflow', + 'PDF_add_thumbnail', + 'PDF_add_weblink', + 'PDF_arc', + 'PDF_arcn', + 'PDF_attach_file', + 'PDF_begin_document', + 'PDF_begin_font', + 'PDF_begin_glyph', + 'PDF_begin_item', + 'PDF_begin_layer', + 'PDF_begin_page', + 'PDF_begin_page_ext', + 'PDF_begin_pattern', + 'PDF_begin_template', + 'PDF_begin_template_ext', + 'PDF_circle', + 'PDF_clip', + 'PDF_close', + 'PDF_close_image', + 'PDF_close_pdi', + 'PDF_close_pdi_page', + 'PDF_closepath', + 'PDF_closepath_fill_stroke', + 'PDF_closepath_stroke', + 'PDF_concat', + 'PDF_continue_text', + 'PDF_create_3dview', + 'PDF_create_action', + 'PDF_create_annotation', + 'PDF_create_bookmark', + 'PDF_create_field', + 'PDF_create_fieldgroup', + 'PDF_create_gstate', + 'PDF_create_pvf', + 'PDF_create_textflow', + 'PDF_curveto', + 'PDF_define_layer', + 'PDF_delete', + 'PDF_delete_pvf', + 'PDF_delete_table', + 'PDF_delete_textflow', + 'PDF_encoding_set_char', + 'PDF_end_document', + 'PDF_end_font', + 'PDF_end_glyph', + 'PDF_end_item', + 'PDF_end_layer', + 'PDF_end_page', + 'PDF_end_page_ext', + 'PDF_end_pattern', + 'PDF_end_template', + 'PDF_endpath', + 'PDF_fill', + 'PDF_fill_imageblock', + 'PDF_fill_pdfblock', + 'PDF_fill_stroke', + 'PDF_fill_textblock', + 'PDF_findfont', + 'PDF_fit_image', + 'PDF_fit_pdi_page', + 'PDF_fit_table', + 'PDF_fit_textflow', + 'PDF_fit_textline', + 'PDF_get_apiname', + 'PDF_get_buffer', + 'PDF_get_errmsg', + 'PDF_get_errnum', + 'PDF_get_parameter', + 'PDF_get_pdi_parameter', + 'PDF_get_pdi_value', + 'PDF_get_value', + 'PDF_info_font', + 'PDF_info_matchbox', + 'PDF_info_table', + 'PDF_info_textflow', + 'PDF_info_textline', + 'PDF_initgraphics', + 'PDF_lineto', + 'PDF_load_3ddata', + 'PDF_load_font', + 'PDF_load_iccprofile', + 'PDF_load_image', + 'PDF_makespotcolor', + 'PDF_moveto', + 'PDF_new', + 'PDF_open_ccitt', + 'PDF_open_file', + 'PDF_open_image', + 'PDF_open_image_file', + 'PDF_open_memory_image', + 'PDF_open_pdi', + 'PDF_open_pdi_document', + 'PDF_open_pdi_page', + 'PDF_pcos_get_number', + 'PDF_pcos_get_stream', + 'PDF_pcos_get_string', + 'PDF_place_image', + 'PDF_place_pdi_page', + 'PDF_process_pdi', + 'PDF_rect', + 'PDF_restore', + 'PDF_resume_page', + 'PDF_rotate', + 'PDF_save', + 'PDF_scale', + 'PDF_set_border_color', + 'PDF_set_border_dash', + 'PDF_set_border_style', + 'PDF_set_gstate', + 'PDF_set_info', + 'PDF_set_layer_dependency', + 'PDF_set_parameter', + 'PDF_set_text_pos', + 'PDF_set_value', + 'PDF_setcolor', + 'PDF_setdash', + 'PDF_setdashpattern', + 'PDF_setflat', + 'PDF_setfont', + 'PDF_setgray', + 'PDF_setgray_fill', + 'PDF_setgray_stroke', + 'PDF_setlinecap', + 'PDF_setlinejoin', + 'PDF_setlinewidth', + 'PDF_setmatrix', + 'PDF_setmiterlimit', + 'PDF_setrgbcolor', + 'PDF_setrgbcolor_fill', + 'PDF_setrgbcolor_stroke', + 'PDF_shading', + 'PDF_shading_pattern', + 'PDF_shfill', + 'PDF_show', + 'PDF_show_boxed', + 'PDF_show_xy', + 'PDF_skew', + 'PDF_stringwidth', + 'PDF_stroke', + 'PDF_suspend_page', + 'PDF_translate', + 'PDF_utf16_to_utf8', + 'PDF_utf32_to_utf16', + 'PDF_utf8_to_utf16', + 'PDO::pgsqlLOBOpen', + 'RarEntry::getStream', + 'SQLite3::openBlob', + 'SWFMovie::saveToFile', + 'SplFileInfo::openFile', + 'SplFileObject::openFile', + 'SplTempFileObject::openFile', + 'V8Js::compileString', + 'V8Js::executeScript', + 'Vtiful\Kernel\Excel::setColumn', + 'Vtiful\Kernel\Excel::setRow', + 'Vtiful\Kernel\Format::align', + 'Vtiful\Kernel\Format::bold', + 'Vtiful\Kernel\Format::italic', + 'Vtiful\Kernel\Format::underline', + 'XMLWriter::openMemory', + 'XMLWriter::openURI', + 'ZipArchive::getStream', + 'Zookeeper::setLogStream', + 'apc_bin_dumpfile', + 'apc_bin_loadfile', + 'bbcode_add_element', + 'bbcode_add_smiley', + 'bbcode_create', + 'bbcode_destroy', + 'bbcode_parse', + 'bbcode_set_arg_parser', + 'bbcode_set_flags', + 'bcompiler_read', + 'bcompiler_write_class', + 'bcompiler_write_constant', + 'bcompiler_write_exe_footer', + 'bcompiler_write_file', + 'bcompiler_write_footer', + 'bcompiler_write_function', + 'bcompiler_write_functions_from_file', + 'bcompiler_write_header', + 'bcompiler_write_included_filename', + 'bzclose', + 'bzerrno', + 'bzerror', + 'bzerrstr', + 'bzflush', + 'bzopen', + 'bzread', + 'bzwrite', + 'cairo_surface_write_to_png', + 'closedir', + 'copy', + 'crack_closedict', + 'crack_opendict', + 'cubrid_bind', + 'cubrid_close_prepare', + 'cubrid_close_request', + 'cubrid_col_get', + 'cubrid_col_size', + 'cubrid_column_names', + 'cubrid_column_types', + 'cubrid_commit', + 'cubrid_connect', + 'cubrid_connect_with_url', + 'cubrid_current_oid', + 'cubrid_db_parameter', + 'cubrid_disconnect', + 'cubrid_drop', + 'cubrid_fetch', + 'cubrid_free_result', + 'cubrid_get', + 'cubrid_get_autocommit', + 'cubrid_get_charset', + 'cubrid_get_class_name', + 'cubrid_get_db_parameter', + 'cubrid_get_query_timeout', + 'cubrid_get_server_info', + 'cubrid_insert_id', + 'cubrid_is_instance', + 'cubrid_lob2_bind', + 'cubrid_lob2_close', + 'cubrid_lob2_export', + 'cubrid_lob2_import', + 'cubrid_lob2_new', + 'cubrid_lob2_read', + 'cubrid_lob2_seek', + 'cubrid_lob2_seek64', + 'cubrid_lob2_size', + 'cubrid_lob2_size64', + 'cubrid_lob2_tell', + 'cubrid_lob2_tell64', + 'cubrid_lob2_write', + 'cubrid_lob_export', + 'cubrid_lob_get', + 'cubrid_lob_send', + 'cubrid_lob_size', + 'cubrid_lock_read', + 'cubrid_lock_write', + 'cubrid_move_cursor', + 'cubrid_next_result', + 'cubrid_num_cols', + 'cubrid_num_rows', + 'cubrid_pconnect', + 'cubrid_pconnect_with_url', + 'cubrid_prepare', + 'cubrid_put', + 'cubrid_query', + 'cubrid_rollback', + 'cubrid_schema', + 'cubrid_seq_add', + 'cubrid_seq_drop', + 'cubrid_seq_insert', + 'cubrid_seq_put', + 'cubrid_set_add', + 'cubrid_set_autocommit', + 'cubrid_set_db_parameter', + 'cubrid_set_drop', + 'cubrid_set_query_timeout', + 'cubrid_unbuffered_query', + 'curl_close', + 'curl_copy_handle', + 'curl_errno', + 'curl_error', + 'curl_escape', + 'curl_exec', + 'curl_getinfo', + 'curl_multi_add_handle', + 'curl_multi_close', + 'curl_multi_errno', + 'curl_multi_exec', + 'curl_multi_getcontent', + 'curl_multi_info_read', + 'curl_multi_remove_handle', + 'curl_multi_select', + 'curl_multi_setopt', + 'curl_pause', + 'curl_reset', + 'curl_setopt', + 'curl_setopt_array', + 'curl_share_close', + 'curl_share_errno', + 'curl_share_init', + 'curl_share_setopt', + 'curl_unescape', + 'cyrus_authenticate', + 'cyrus_bind', + 'cyrus_close', + 'cyrus_connect', + 'cyrus_query', + 'cyrus_unbind', + 'db2_autocommit', + 'db2_bind_param', + 'db2_client_info', + 'db2_close', + 'db2_column_privileges', + 'db2_columns', + 'db2_commit', + 'db2_conn_error', + 'db2_conn_errormsg', + 'db2_connect', + 'db2_cursor_type', + 'db2_exec', + 'db2_execute', + 'db2_fetch_array', + 'db2_fetch_assoc', + 'db2_fetch_both', + 'db2_fetch_object', + 'db2_fetch_row', + 'db2_field_display_size', + 'db2_field_name', + 'db2_field_num', + 'db2_field_precision', + 'db2_field_scale', + 'db2_field_type', + 'db2_field_width', + 'db2_foreign_keys', + 'db2_free_result', + 'db2_free_stmt', + 'db2_get_option', + 'db2_last_insert_id', + 'db2_lob_read', + 'db2_next_result', + 'db2_num_fields', + 'db2_num_rows', + 'db2_pclose', + 'db2_pconnect', + 'db2_prepare', + 'db2_primary_keys', + 'db2_procedure_columns', + 'db2_procedures', + 'db2_result', + 'db2_rollback', + 'db2_server_info', + 'db2_set_option', + 'db2_special_columns', + 'db2_statistics', + 'db2_stmt_error', + 'db2_stmt_errormsg', + 'db2_table_privileges', + 'db2_tables', + 'dba_close', + 'dba_delete', + 'dba_exists', + 'dba_fetch', + 'dba_firstkey', + 'dba_insert', + 'dba_nextkey', + 'dba_open', + 'dba_optimize', + 'dba_popen', + 'dba_replace', + 'dba_sync', + 'dbplus_add', + 'dbplus_aql', + 'dbplus_close', + 'dbplus_curr', + 'dbplus_find', + 'dbplus_first', + 'dbplus_flush', + 'dbplus_freelock', + 'dbplus_freerlocks', + 'dbplus_getlock', + 'dbplus_getunique', + 'dbplus_info', + 'dbplus_last', + 'dbplus_lockrel', + 'dbplus_next', + 'dbplus_open', + 'dbplus_prev', + 'dbplus_rchperm', + 'dbplus_rcreate', + 'dbplus_rcrtexact', + 'dbplus_rcrtlike', + 'dbplus_restorepos', + 'dbplus_rkeys', + 'dbplus_ropen', + 'dbplus_rquery', + 'dbplus_rrename', + 'dbplus_rsecindex', + 'dbplus_runlink', + 'dbplus_rzap', + 'dbplus_savepos', + 'dbplus_setindex', + 'dbplus_setindexbynumber', + 'dbplus_sql', + 'dbplus_tremove', + 'dbplus_undo', + 'dbplus_undoprepare', + 'dbplus_unlockrel', + 'dbplus_unselect', + 'dbplus_update', + 'dbplus_xlockrel', + 'dbplus_xunlockrel', + 'deflate_add', + 'dio_close', + 'dio_fcntl', + 'dio_open', + 'dio_read', + 'dio_seek', + 'dio_stat', + 'dio_tcsetattr', + 'dio_truncate', + 'dio_write', + 'dir', + 'eio_busy', + 'eio_cancel', + 'eio_chmod', + 'eio_chown', + 'eio_close', + 'eio_custom', + 'eio_dup2', + 'eio_fallocate', + 'eio_fchmod', + 'eio_fchown', + 'eio_fdatasync', + 'eio_fstat', + 'eio_fstatvfs', + 'eio_fsync', + 'eio_ftruncate', + 'eio_futime', + 'eio_get_last_error', + 'eio_grp', + 'eio_grp_add', + 'eio_grp_cancel', + 'eio_grp_limit', + 'eio_link', + 'eio_lstat', + 'eio_mkdir', + 'eio_mknod', + 'eio_nop', + 'eio_open', + 'eio_read', + 'eio_readahead', + 'eio_readdir', + 'eio_readlink', + 'eio_realpath', + 'eio_rename', + 'eio_rmdir', + 'eio_seek', + 'eio_sendfile', + 'eio_stat', + 'eio_statvfs', + 'eio_symlink', + 'eio_sync', + 'eio_sync_file_range', + 'eio_syncfs', + 'eio_truncate', + 'eio_unlink', + 'eio_utime', + 'eio_write', + 'enchant_broker_describe', + 'enchant_broker_dict_exists', + 'enchant_broker_free', + 'enchant_broker_free_dict', + 'enchant_broker_get_dict_path', + 'enchant_broker_get_error', + 'enchant_broker_init', + 'enchant_broker_list_dicts', + 'enchant_broker_request_dict', + 'enchant_broker_request_pwl_dict', + 'enchant_broker_set_dict_path', + 'enchant_broker_set_ordering', + 'enchant_dict_add_to_personal', + 'enchant_dict_add_to_session', + 'enchant_dict_check', + 'enchant_dict_describe', + 'enchant_dict_get_error', + 'enchant_dict_is_in_session', + 'enchant_dict_quick_check', + 'enchant_dict_store_replacement', + 'enchant_dict_suggest', + 'event_add', + 'event_base_free', + 'event_base_loop', + 'event_base_loopbreak', + 'event_base_loopexit', + 'event_base_new', + 'event_base_priority_init', + 'event_base_reinit', + 'event_base_set', + 'event_buffer_base_set', + 'event_buffer_disable', + 'event_buffer_enable', + 'event_buffer_fd_set', + 'event_buffer_free', + 'event_buffer_new', + 'event_buffer_priority_set', + 'event_buffer_read', + 'event_buffer_set_callback', + 'event_buffer_timeout_set', + 'event_buffer_watermark_set', + 'event_buffer_write', + 'event_del', + 'event_free', + 'event_new', + 'event_priority_set', + 'event_set', + 'event_timer_add', + 'event_timer_del', + 'event_timer_pending', + 'event_timer_set', + 'expect_expectl', + 'expect_popen', + 'fam_cancel_monitor', + 'fam_close', + 'fam_monitor_collection', + 'fam_monitor_directory', + 'fam_monitor_file', + 'fam_next_event', + 'fam_open', + 'fam_pending', + 'fam_resume_monitor', + 'fam_suspend_monitor', + 'fann_cascadetrain_on_data', + 'fann_cascadetrain_on_file', + 'fann_clear_scaling_params', + 'fann_copy', + 'fann_create_from_file', + 'fann_create_shortcut_array', + 'fann_create_standard', + 'fann_create_standard_array', + 'fann_create_train', + 'fann_create_train_from_callback', + 'fann_descale_input', + 'fann_descale_output', + 'fann_descale_train', + 'fann_destroy', + 'fann_destroy_train', + 'fann_duplicate_train_data', + 'fann_get_MSE', + 'fann_get_activation_function', + 'fann_get_activation_steepness', + 'fann_get_bias_array', + 'fann_get_bit_fail', + 'fann_get_bit_fail_limit', + 'fann_get_cascade_activation_functions', + 'fann_get_cascade_activation_functions_count', + 'fann_get_cascade_activation_steepnesses', + 'fann_get_cascade_activation_steepnesses_count', + 'fann_get_cascade_candidate_change_fraction', + 'fann_get_cascade_candidate_limit', + 'fann_get_cascade_candidate_stagnation_epochs', + 'fann_get_cascade_max_cand_epochs', + 'fann_get_cascade_max_out_epochs', + 'fann_get_cascade_min_cand_epochs', + 'fann_get_cascade_min_out_epochs', + 'fann_get_cascade_num_candidate_groups', + 'fann_get_cascade_num_candidates', + 'fann_get_cascade_output_change_fraction', + 'fann_get_cascade_output_stagnation_epochs', + 'fann_get_cascade_weight_multiplier', + 'fann_get_connection_array', + 'fann_get_connection_rate', + 'fann_get_errno', + 'fann_get_errstr', + 'fann_get_layer_array', + 'fann_get_learning_momentum', + 'fann_get_learning_rate', + 'fann_get_network_type', + 'fann_get_num_input', + 'fann_get_num_layers', + 'fann_get_num_output', + 'fann_get_quickprop_decay', + 'fann_get_quickprop_mu', + 'fann_get_rprop_decrease_factor', + 'fann_get_rprop_delta_max', + 'fann_get_rprop_delta_min', + 'fann_get_rprop_delta_zero', + 'fann_get_rprop_increase_factor', + 'fann_get_sarprop_step_error_shift', + 'fann_get_sarprop_step_error_threshold_factor', + 'fann_get_sarprop_temperature', + 'fann_get_sarprop_weight_decay_shift', + 'fann_get_total_connections', + 'fann_get_total_neurons', + 'fann_get_train_error_function', + 'fann_get_train_stop_function', + 'fann_get_training_algorithm', + 'fann_init_weights', + 'fann_length_train_data', + 'fann_merge_train_data', + 'fann_num_input_train_data', + 'fann_num_output_train_data', + 'fann_randomize_weights', + 'fann_read_train_from_file', + 'fann_reset_errno', + 'fann_reset_errstr', + 'fann_run', + 'fann_save', + 'fann_save_train', + 'fann_scale_input', + 'fann_scale_input_train_data', + 'fann_scale_output', + 'fann_scale_output_train_data', + 'fann_scale_train', + 'fann_scale_train_data', + 'fann_set_activation_function', + 'fann_set_activation_function_hidden', + 'fann_set_activation_function_layer', + 'fann_set_activation_function_output', + 'fann_set_activation_steepness', + 'fann_set_activation_steepness_hidden', + 'fann_set_activation_steepness_layer', + 'fann_set_activation_steepness_output', + 'fann_set_bit_fail_limit', + 'fann_set_callback', + 'fann_set_cascade_activation_functions', + 'fann_set_cascade_activation_steepnesses', + 'fann_set_cascade_candidate_change_fraction', + 'fann_set_cascade_candidate_limit', + 'fann_set_cascade_candidate_stagnation_epochs', + 'fann_set_cascade_max_cand_epochs', + 'fann_set_cascade_max_out_epochs', + 'fann_set_cascade_min_cand_epochs', + 'fann_set_cascade_min_out_epochs', + 'fann_set_cascade_num_candidate_groups', + 'fann_set_cascade_output_change_fraction', + 'fann_set_cascade_output_stagnation_epochs', + 'fann_set_cascade_weight_multiplier', + 'fann_set_error_log', + 'fann_set_input_scaling_params', + 'fann_set_learning_momentum', + 'fann_set_learning_rate', + 'fann_set_output_scaling_params', + 'fann_set_quickprop_decay', + 'fann_set_quickprop_mu', + 'fann_set_rprop_decrease_factor', + 'fann_set_rprop_delta_max', + 'fann_set_rprop_delta_min', + 'fann_set_rprop_delta_zero', + 'fann_set_rprop_increase_factor', + 'fann_set_sarprop_step_error_shift', + 'fann_set_sarprop_step_error_threshold_factor', + 'fann_set_sarprop_temperature', + 'fann_set_sarprop_weight_decay_shift', + 'fann_set_scaling_params', + 'fann_set_train_error_function', + 'fann_set_train_stop_function', + 'fann_set_training_algorithm', + 'fann_set_weight', + 'fann_set_weight_array', + 'fann_shuffle_train_data', + 'fann_subset_train_data', + 'fann_test', + 'fann_test_data', + 'fann_train', + 'fann_train_epoch', + 'fann_train_on_data', + 'fann_train_on_file', + 'fbsql_affected_rows', + 'fbsql_autocommit', + 'fbsql_blob_size', + 'fbsql_change_user', + 'fbsql_clob_size', + 'fbsql_close', + 'fbsql_commit', + 'fbsql_connect', + 'fbsql_create_blob', + 'fbsql_create_clob', + 'fbsql_create_db', + 'fbsql_data_seek', + 'fbsql_database', + 'fbsql_database_password', + 'fbsql_db_query', + 'fbsql_db_status', + 'fbsql_drop_db', + 'fbsql_errno', + 'fbsql_error', + 'fbsql_fetch_array', + 'fbsql_fetch_assoc', + 'fbsql_fetch_field', + 'fbsql_fetch_lengths', + 'fbsql_fetch_object', + 'fbsql_fetch_row', + 'fbsql_field_flags', + 'fbsql_field_len', + 'fbsql_field_name', + 'fbsql_field_seek', + 'fbsql_field_table', + 'fbsql_field_type', + 'fbsql_free_result', + 'fbsql_get_autostart_info', + 'fbsql_hostname', + 'fbsql_insert_id', + 'fbsql_list_dbs', + 'fbsql_list_fields', + 'fbsql_list_tables', + 'fbsql_next_result', + 'fbsql_num_fields', + 'fbsql_num_rows', + 'fbsql_password', + 'fbsql_pconnect', + 'fbsql_query', + 'fbsql_read_blob', + 'fbsql_read_clob', + 'fbsql_result', + 'fbsql_rollback', + 'fbsql_rows_fetched', + 'fbsql_select_db', + 'fbsql_set_characterset', + 'fbsql_set_lob_mode', + 'fbsql_set_password', + 'fbsql_set_transaction', + 'fbsql_start_db', + 'fbsql_stop_db', + 'fbsql_table_name', + 'fbsql_username', + 'fclose', + 'fdf_add_doc_javascript', + 'fdf_add_template', + 'fdf_close', + 'fdf_create', + 'fdf_enum_values', + 'fdf_get_ap', + 'fdf_get_attachment', + 'fdf_get_encoding', + 'fdf_get_file', + 'fdf_get_flags', + 'fdf_get_opt', + 'fdf_get_status', + 'fdf_get_value', + 'fdf_get_version', + 'fdf_next_field_name', + 'fdf_open', + 'fdf_open_string', + 'fdf_remove_item', + 'fdf_save', + 'fdf_save_string', + 'fdf_set_ap', + 'fdf_set_encoding', + 'fdf_set_file', + 'fdf_set_flags', + 'fdf_set_javascript_action', + 'fdf_set_on_import_javascript', + 'fdf_set_opt', + 'fdf_set_status', + 'fdf_set_submit_form_action', + 'fdf_set_target_frame', + 'fdf_set_value', + 'fdf_set_version', + 'feof', + 'fflush', + 'ffmpeg_frame::__construct', + 'ffmpeg_frame::toGDImage', + 'fgetc', + 'fgetcsv', + 'fgets', + 'fgetss', + 'file', + 'file_get_contents', + 'file_put_contents', + 'finfo::buffer', + 'finfo::file', + 'finfo_buffer', + 'finfo_close', + 'finfo_file', + 'finfo_open', + 'finfo_set_flags', + 'flock', + 'fopen', + 'fpassthru', + 'fprintf', + 'fputcsv', + 'fputs', + 'fread', + 'fscanf', + 'fseek', + 'fstat', + 'ftell', + 'ftp_alloc', + 'ftp_append', + 'ftp_cdup', + 'ftp_chdir', + 'ftp_chmod', + 'ftp_close', + 'ftp_delete', + 'ftp_exec', + 'ftp_fget', + 'ftp_fput', + 'ftp_get', + 'ftp_get_option', + 'ftp_login', + 'ftp_mdtm', + 'ftp_mkdir', + 'ftp_mlsd', + 'ftp_nb_continue', + 'ftp_nb_fget', + 'ftp_nb_fput', + 'ftp_nb_get', + 'ftp_nb_put', + 'ftp_nlist', + 'ftp_pasv', + 'ftp_put', + 'ftp_pwd', + 'ftp_quit', + 'ftp_raw', + 'ftp_rawlist', + 'ftp_rename', + 'ftp_rmdir', + 'ftp_set_option', + 'ftp_site', + 'ftp_size', + 'ftp_systype', + 'ftruncate', + 'fwrite', + 'get_resource_type', + 'gmp_div', + 'gnupg::init', + 'gnupg_adddecryptkey', + 'gnupg_addencryptkey', + 'gnupg_addsignkey', + 'gnupg_cleardecryptkeys', + 'gnupg_clearencryptkeys', + 'gnupg_clearsignkeys', + 'gnupg_decrypt', + 'gnupg_decryptverify', + 'gnupg_encrypt', + 'gnupg_encryptsign', + 'gnupg_export', + 'gnupg_geterror', + 'gnupg_getprotocol', + 'gnupg_import', + 'gnupg_init', + 'gnupg_keyinfo', + 'gnupg_setarmor', + 'gnupg_seterrormode', + 'gnupg_setsignmode', + 'gnupg_sign', + 'gnupg_verify', + 'gupnp_context_get_host_ip', + 'gupnp_context_get_port', + 'gupnp_context_get_subscription_timeout', + 'gupnp_context_host_path', + 'gupnp_context_new', + 'gupnp_context_set_subscription_timeout', + 'gupnp_context_timeout_add', + 'gupnp_context_unhost_path', + 'gupnp_control_point_browse_start', + 'gupnp_control_point_browse_stop', + 'gupnp_control_point_callback_set', + 'gupnp_control_point_new', + 'gupnp_device_action_callback_set', + 'gupnp_device_info_get', + 'gupnp_device_info_get_service', + 'gupnp_root_device_get_available', + 'gupnp_root_device_get_relative_location', + 'gupnp_root_device_new', + 'gupnp_root_device_set_available', + 'gupnp_root_device_start', + 'gupnp_root_device_stop', + 'gupnp_service_action_get', + 'gupnp_service_action_return', + 'gupnp_service_action_return_error', + 'gupnp_service_action_set', + 'gupnp_service_freeze_notify', + 'gupnp_service_info_get', + 'gupnp_service_info_get_introspection', + 'gupnp_service_introspection_get_state_variable', + 'gupnp_service_notify', + 'gupnp_service_proxy_action_get', + 'gupnp_service_proxy_action_set', + 'gupnp_service_proxy_add_notify', + 'gupnp_service_proxy_callback_set', + 'gupnp_service_proxy_get_subscribed', + 'gupnp_service_proxy_remove_notify', + 'gupnp_service_proxy_send_action', + 'gupnp_service_proxy_set_subscribed', + 'gupnp_service_thaw_notify', + 'gzclose', + 'gzeof', + 'gzgetc', + 'gzgets', + 'gzgetss', + 'gzpassthru', + 'gzputs', + 'gzread', + 'gzrewind', + 'gzseek', + 'gztell', + 'gzwrite', + 'hash_update_stream', + 'http\Env\Response::send', + 'http_get_request_body_stream', + 'ibase_add_user', + 'ibase_affected_rows', + 'ibase_backup', + 'ibase_blob_add', + 'ibase_blob_cancel', + 'ibase_blob_close', + 'ibase_blob_create', + 'ibase_blob_get', + 'ibase_blob_open', + 'ibase_close', + 'ibase_commit', + 'ibase_commit_ret', + 'ibase_connect', + 'ibase_db_info', + 'ibase_delete_user', + 'ibase_drop_db', + 'ibase_execute', + 'ibase_fetch_assoc', + 'ibase_fetch_object', + 'ibase_fetch_row', + 'ibase_field_info', + 'ibase_free_event_handler', + 'ibase_free_query', + 'ibase_free_result', + 'ibase_gen_id', + 'ibase_maintain_db', + 'ibase_modify_user', + 'ibase_name_result', + 'ibase_num_fields', + 'ibase_num_params', + 'ibase_param_info', + 'ibase_pconnect', + 'ibase_prepare', + 'ibase_query', + 'ibase_restore', + 'ibase_rollback', + 'ibase_rollback_ret', + 'ibase_server_info', + 'ibase_service_attach', + 'ibase_service_detach', + 'ibase_set_event_handler', + 'ibase_trans', + 'ifx_affected_rows', + 'ifx_close', + 'ifx_connect', + 'ifx_do', + 'ifx_error', + 'ifx_fetch_row', + 'ifx_fieldproperties', + 'ifx_fieldtypes', + 'ifx_free_result', + 'ifx_getsqlca', + 'ifx_htmltbl_result', + 'ifx_num_fields', + 'ifx_num_rows', + 'ifx_pconnect', + 'ifx_prepare', + 'ifx_query', + 'image2wbmp', + 'imageaffine', + 'imagealphablending', + 'imageantialias', + 'imagearc', + 'imagebmp', + 'imagechar', + 'imagecharup', + 'imagecolorallocate', + 'imagecolorallocatealpha', + 'imagecolorat', + 'imagecolorclosest', + 'imagecolorclosestalpha', + 'imagecolorclosesthwb', + 'imagecolordeallocate', + 'imagecolorexact', + 'imagecolorexactalpha', + 'imagecolormatch', + 'imagecolorresolve', + 'imagecolorresolvealpha', + 'imagecolorset', + 'imagecolorsforindex', + 'imagecolorstotal', + 'imagecolortransparent', + 'imageconvolution', + 'imagecopy', + 'imagecopymerge', + 'imagecopymergegray', + 'imagecopyresampled', + 'imagecopyresized', + 'imagecrop', + 'imagecropauto', + 'imagedashedline', + 'imagedestroy', + 'imageellipse', + 'imagefill', + 'imagefilledarc', + 'imagefilledellipse', + 'imagefilledpolygon', + 'imagefilledrectangle', + 'imagefilltoborder', + 'imagefilter', + 'imageflip', + 'imagefttext', + 'imagegammacorrect', + 'imagegd', + 'imagegd2', + 'imagegetclip', + 'imagegif', + 'imagegrabscreen', + 'imagegrabwindow', + 'imageinterlace', + 'imageistruecolor', + 'imagejpeg', + 'imagelayereffect', + 'imageline', + 'imageopenpolygon', + 'imagepalettecopy', + 'imagepalettetotruecolor', + 'imagepng', + 'imagepolygon', + 'imagepsencodefont', + 'imagepsextendfont', + 'imagepsfreefont', + 'imagepsloadfont', + 'imagepsslantfont', + 'imagepstext', + 'imagerectangle', + 'imageresolution', + 'imagerotate', + 'imagesavealpha', + 'imagescale', + 'imagesetbrush', + 'imagesetclip', + 'imagesetinterpolation', + 'imagesetpixel', + 'imagesetstyle', + 'imagesetthickness', + 'imagesettile', + 'imagestring', + 'imagestringup', + 'imagesx', + 'imagesy', + 'imagetruecolortopalette', + 'imagettftext', + 'imagewbmp', + 'imagewebp', + 'imagexbm', + 'imap_append', + 'imap_body', + 'imap_bodystruct', + 'imap_check', + 'imap_clearflag_full', + 'imap_close', + 'imap_create', + 'imap_createmailbox', + 'imap_delete', + 'imap_deletemailbox', + 'imap_expunge', + 'imap_fetch_overview', + 'imap_fetchbody', + 'imap_fetchheader', + 'imap_fetchmime', + 'imap_fetchstructure', + 'imap_fetchtext', + 'imap_gc', + 'imap_get_quota', + 'imap_get_quotaroot', + 'imap_getacl', + 'imap_getmailboxes', + 'imap_getsubscribed', + 'imap_header', + 'imap_headerinfo', + 'imap_headers', + 'imap_list', + 'imap_listmailbox', + 'imap_listscan', + 'imap_listsubscribed', + 'imap_lsub', + 'imap_mail_copy', + 'imap_mail_move', + 'imap_mailboxmsginfo', + 'imap_msgno', + 'imap_num_msg', + 'imap_num_recent', + 'imap_ping', + 'imap_rename', + 'imap_renamemailbox', + 'imap_reopen', + 'imap_savebody', + 'imap_scan', + 'imap_scanmailbox', + 'imap_search', + 'imap_set_quota', + 'imap_setacl', + 'imap_setflag_full', + 'imap_sort', + 'imap_status', + 'imap_subscribe', + 'imap_thread', + 'imap_uid', + 'imap_undelete', + 'imap_unsubscribe', + 'inflate_add', + 'inflate_get_read_len', + 'inflate_get_status', + 'ingres_autocommit', + 'ingres_autocommit_state', + 'ingres_charset', + 'ingres_close', + 'ingres_commit', + 'ingres_connect', + 'ingres_cursor', + 'ingres_errno', + 'ingres_error', + 'ingres_errsqlstate', + 'ingres_escape_string', + 'ingres_execute', + 'ingres_fetch_array', + 'ingres_fetch_assoc', + 'ingres_fetch_object', + 'ingres_fetch_proc_return', + 'ingres_fetch_row', + 'ingres_field_length', + 'ingres_field_name', + 'ingres_field_nullable', + 'ingres_field_precision', + 'ingres_field_scale', + 'ingres_field_type', + 'ingres_free_result', + 'ingres_next_error', + 'ingres_num_fields', + 'ingres_num_rows', + 'ingres_pconnect', + 'ingres_prepare', + 'ingres_query', + 'ingres_result_seek', + 'ingres_rollback', + 'ingres_set_environment', + 'ingres_unbuffered_query', + 'inotify_add_watch', + 'inotify_init', + 'inotify_queue_len', + 'inotify_read', + 'inotify_rm_watch', + 'kadm5_chpass_principal', + 'kadm5_create_principal', + 'kadm5_delete_principal', + 'kadm5_destroy', + 'kadm5_flush', + 'kadm5_get_policies', + 'kadm5_get_principal', + 'kadm5_get_principals', + 'kadm5_init_with_password', + 'kadm5_modify_principal', + 'ldap_add', + 'ldap_bind', + 'ldap_close', + 'ldap_compare', + 'ldap_control_paged_result', + 'ldap_control_paged_result_response', + 'ldap_count_entries', + 'ldap_delete', + 'ldap_errno', + 'ldap_error', + 'ldap_exop', + 'ldap_exop_passwd', + 'ldap_exop_refresh', + 'ldap_exop_whoami', + 'ldap_first_attribute', + 'ldap_first_entry', + 'ldap_first_reference', + 'ldap_free_result', + 'ldap_get_attributes', + 'ldap_get_dn', + 'ldap_get_entries', + 'ldap_get_option', + 'ldap_get_values', + 'ldap_get_values_len', + 'ldap_mod_add', + 'ldap_mod_del', + 'ldap_mod_replace', + 'ldap_modify', + 'ldap_modify_batch', + 'ldap_next_attribute', + 'ldap_next_entry', + 'ldap_next_reference', + 'ldap_parse_exop', + 'ldap_parse_reference', + 'ldap_parse_result', + 'ldap_rename', + 'ldap_sasl_bind', + 'ldap_set_option', + 'ldap_set_rebind_proc', + 'ldap_sort', + 'ldap_start_tls', + 'ldap_unbind', + 'libxml_set_streams_context', + 'm_checkstatus', + 'm_completeauthorizations', + 'm_connect', + 'm_connectionerror', + 'm_deletetrans', + 'm_destroyconn', + 'm_getcell', + 'm_getcellbynum', + 'm_getcommadelimited', + 'm_getheader', + 'm_initconn', + 'm_iscommadelimited', + 'm_maxconntimeout', + 'm_monitor', + 'm_numcolumns', + 'm_numrows', + 'm_parsecommadelimited', + 'm_responsekeys', + 'm_responseparam', + 'm_returnstatus', + 'm_setblocking', + 'm_setdropfile', + 'm_setip', + 'm_setssl', + 'm_setssl_cafile', + 'm_setssl_files', + 'm_settimeout', + 'm_transactionssent', + 'm_transinqueue', + 'm_transkeyval', + 'm_transnew', + 'm_transsend', + 'm_validateidentifier', + 'm_verifyconnection', + 'm_verifysslcert', + 'mailparse_determine_best_xfer_encoding', + 'mailparse_msg_create', + 'mailparse_msg_extract_part', + 'mailparse_msg_extract_part_file', + 'mailparse_msg_extract_whole_part_file', + 'mailparse_msg_free', + 'mailparse_msg_get_part', + 'mailparse_msg_get_part_data', + 'mailparse_msg_get_structure', + 'mailparse_msg_parse', + 'mailparse_msg_parse_file', + 'mailparse_stream_encode', + 'mailparse_uudecode_all', + 'maxdb::use_result', + 'maxdb_affected_rows', + 'maxdb_connect', + 'maxdb_disable_rpl_parse', + 'maxdb_dump_debug_info', + 'maxdb_embedded_connect', + 'maxdb_enable_reads_from_master', + 'maxdb_enable_rpl_parse', + 'maxdb_errno', + 'maxdb_error', + 'maxdb_fetch_lengths', + 'maxdb_field_tell', + 'maxdb_get_host_info', + 'maxdb_get_proto_info', + 'maxdb_get_server_info', + 'maxdb_get_server_version', + 'maxdb_info', + 'maxdb_init', + 'maxdb_insert_id', + 'maxdb_master_query', + 'maxdb_more_results', + 'maxdb_next_result', + 'maxdb_num_fields', + 'maxdb_num_rows', + 'maxdb_rpl_parse_enabled', + 'maxdb_rpl_probe', + 'maxdb_select_db', + 'maxdb_sqlstate', + 'maxdb_stmt::result_metadata', + 'maxdb_stmt_affected_rows', + 'maxdb_stmt_errno', + 'maxdb_stmt_error', + 'maxdb_stmt_num_rows', + 'maxdb_stmt_param_count', + 'maxdb_stmt_result_metadata', + 'maxdb_stmt_sqlstate', + 'maxdb_thread_id', + 'maxdb_use_result', + 'maxdb_warning_count', + 'mcrypt_enc_get_algorithms_name', + 'mcrypt_enc_get_block_size', + 'mcrypt_enc_get_iv_size', + 'mcrypt_enc_get_key_size', + 'mcrypt_enc_get_modes_name', + 'mcrypt_enc_get_supported_key_sizes', + 'mcrypt_enc_is_block_algorithm', + 'mcrypt_enc_is_block_algorithm_mode', + 'mcrypt_enc_is_block_mode', + 'mcrypt_enc_self_test', + 'mcrypt_generic', + 'mcrypt_generic_deinit', + 'mcrypt_generic_end', + 'mcrypt_generic_init', + 'mcrypt_module_close', + 'mcrypt_module_open', + 'mdecrypt_generic', + 'mkdir', + 'mqseries_back', + 'mqseries_begin', + 'mqseries_close', + 'mqseries_cmit', + 'mqseries_conn', + 'mqseries_connx', + 'mqseries_disc', + 'mqseries_get', + 'mqseries_inq', + 'mqseries_open', + 'mqseries_put', + 'mqseries_put1', + 'mqseries_set', + 'msg_get_queue', + 'msg_receive', + 'msg_remove_queue', + 'msg_send', + 'msg_set_queue', + 'msg_stat_queue', + 'msql_affected_rows', + 'msql_close', + 'msql_connect', + 'msql_create_db', + 'msql_data_seek', + 'msql_db_query', + 'msql_drop_db', + 'msql_fetch_array', + 'msql_fetch_field', + 'msql_fetch_object', + 'msql_fetch_row', + 'msql_field_flags', + 'msql_field_len', + 'msql_field_name', + 'msql_field_seek', + 'msql_field_table', + 'msql_field_type', + 'msql_free_result', + 'msql_list_dbs', + 'msql_list_fields', + 'msql_list_tables', + 'msql_num_fields', + 'msql_num_rows', + 'msql_pconnect', + 'msql_query', + 'msql_result', + 'msql_select_db', + 'mssql_bind', + 'mssql_close', + 'mssql_connect', + 'mssql_data_seek', + 'mssql_execute', + 'mssql_fetch_array', + 'mssql_fetch_assoc', + 'mssql_fetch_batch', + 'mssql_fetch_field', + 'mssql_fetch_object', + 'mssql_fetch_row', + 'mssql_field_length', + 'mssql_field_name', + 'mssql_field_seek', + 'mssql_field_type', + 'mssql_free_result', + 'mssql_free_statement', + 'mssql_init', + 'mssql_next_result', + 'mssql_num_fields', + 'mssql_num_rows', + 'mssql_pconnect', + 'mssql_query', + 'mssql_result', + 'mssql_rows_affected', + 'mssql_select_db', + 'mysql_affected_rows', + 'mysql_client_encoding', + 'mysql_close', + 'mysql_connect', + 'mysql_create_db', + 'mysql_data_seek', + 'mysql_db_name', + 'mysql_db_query', + 'mysql_drop_db', + 'mysql_errno', + 'mysql_error', + 'mysql_fetch_array', + 'mysql_fetch_assoc', + 'mysql_fetch_field', + 'mysql_fetch_lengths', + 'mysql_fetch_object', + 'mysql_fetch_row', + 'mysql_field_flags', + 'mysql_field_len', + 'mysql_field_name', + 'mysql_field_seek', + 'mysql_field_table', + 'mysql_field_type', + 'mysql_free_result', + 'mysql_get_host_info', + 'mysql_get_proto_info', + 'mysql_get_server_info', + 'mysql_info', + 'mysql_insert_id', + 'mysql_list_dbs', + 'mysql_list_fields', + 'mysql_list_processes', + 'mysql_list_tables', + 'mysql_num_fields', + 'mysql_num_rows', + 'mysql_pconnect', + 'mysql_ping', + 'mysql_query', + 'mysql_real_escape_string', + 'mysql_result', + 'mysql_select_db', + 'mysql_set_charset', + 'mysql_stat', + 'mysql_tablename', + 'mysql_thread_id', + 'mysql_unbuffered_query', + 'mysqlnd_uh_convert_to_mysqlnd', + 'ncurses_bottom_panel', + 'ncurses_del_panel', + 'ncurses_delwin', + 'ncurses_getmaxyx', + 'ncurses_getyx', + 'ncurses_hide_panel', + 'ncurses_keypad', + 'ncurses_meta', + 'ncurses_move_panel', + 'ncurses_mvwaddstr', + 'ncurses_new_panel', + 'ncurses_newpad', + 'ncurses_newwin', + 'ncurses_panel_above', + 'ncurses_panel_below', + 'ncurses_panel_window', + 'ncurses_pnoutrefresh', + 'ncurses_prefresh', + 'ncurses_replace_panel', + 'ncurses_show_panel', + 'ncurses_top_panel', + 'ncurses_waddch', + 'ncurses_waddstr', + 'ncurses_wattroff', + 'ncurses_wattron', + 'ncurses_wattrset', + 'ncurses_wborder', + 'ncurses_wclear', + 'ncurses_wcolor_set', + 'ncurses_werase', + 'ncurses_wgetch', + 'ncurses_whline', + 'ncurses_wmouse_trafo', + 'ncurses_wmove', + 'ncurses_wnoutrefresh', + 'ncurses_wrefresh', + 'ncurses_wstandend', + 'ncurses_wstandout', + 'ncurses_wvline', + 'newt_button', + 'newt_button_bar', + 'newt_checkbox', + 'newt_checkbox_get_value', + 'newt_checkbox_set_flags', + 'newt_checkbox_set_value', + 'newt_checkbox_tree', + 'newt_checkbox_tree_add_item', + 'newt_checkbox_tree_find_item', + 'newt_checkbox_tree_get_current', + 'newt_checkbox_tree_get_entry_value', + 'newt_checkbox_tree_get_multi_selection', + 'newt_checkbox_tree_get_selection', + 'newt_checkbox_tree_multi', + 'newt_checkbox_tree_set_current', + 'newt_checkbox_tree_set_entry', + 'newt_checkbox_tree_set_entry_value', + 'newt_checkbox_tree_set_width', + 'newt_compact_button', + 'newt_component_add_callback', + 'newt_component_takes_focus', + 'newt_create_grid', + 'newt_draw_form', + 'newt_entry', + 'newt_entry_get_value', + 'newt_entry_set', + 'newt_entry_set_filter', + 'newt_entry_set_flags', + 'newt_form', + 'newt_form_add_component', + 'newt_form_add_components', + 'newt_form_add_hot_key', + 'newt_form_destroy', + 'newt_form_get_current', + 'newt_form_run', + 'newt_form_set_background', + 'newt_form_set_height', + 'newt_form_set_size', + 'newt_form_set_timer', + 'newt_form_set_width', + 'newt_form_watch_fd', + 'newt_grid_add_components_to_form', + 'newt_grid_basic_window', + 'newt_grid_free', + 'newt_grid_get_size', + 'newt_grid_h_close_stacked', + 'newt_grid_h_stacked', + 'newt_grid_place', + 'newt_grid_set_field', + 'newt_grid_simple_window', + 'newt_grid_v_close_stacked', + 'newt_grid_v_stacked', + 'newt_grid_wrapped_window', + 'newt_grid_wrapped_window_at', + 'newt_label', + 'newt_label_set_text', + 'newt_listbox', + 'newt_listbox_append_entry', + 'newt_listbox_clear', + 'newt_listbox_clear_selection', + 'newt_listbox_delete_entry', + 'newt_listbox_get_current', + 'newt_listbox_get_selection', + 'newt_listbox_insert_entry', + 'newt_listbox_item_count', + 'newt_listbox_select_item', + 'newt_listbox_set_current', + 'newt_listbox_set_current_by_key', + 'newt_listbox_set_data', + 'newt_listbox_set_entry', + 'newt_listbox_set_width', + 'newt_listitem', + 'newt_listitem_get_data', + 'newt_listitem_set', + 'newt_radio_get_current', + 'newt_radiobutton', + 'newt_run_form', + 'newt_scale', + 'newt_scale_set', + 'newt_scrollbar_set', + 'newt_textbox', + 'newt_textbox_get_num_lines', + 'newt_textbox_reflowed', + 'newt_textbox_set_height', + 'newt_textbox_set_text', + 'newt_vertical_scrollbar', + 'oci_bind_array_by_name', + 'oci_bind_by_name', + 'oci_cancel', + 'oci_close', + 'oci_commit', + 'oci_connect', + 'oci_define_by_name', + 'oci_error', + 'oci_execute', + 'oci_fetch', + 'oci_fetch_all', + 'oci_fetch_array', + 'oci_fetch_assoc', + 'oci_fetch_object', + 'oci_fetch_row', + 'oci_field_is_null', + 'oci_field_name', + 'oci_field_precision', + 'oci_field_scale', + 'oci_field_size', + 'oci_field_type', + 'oci_field_type_raw', + 'oci_free_cursor', + 'oci_free_statement', + 'oci_get_implicit_resultset', + 'oci_new_collection', + 'oci_new_connect', + 'oci_new_cursor', + 'oci_new_descriptor', + 'oci_num_fields', + 'oci_num_rows', + 'oci_parse', + 'oci_pconnect', + 'oci_register_taf_callback', + 'oci_result', + 'oci_rollback', + 'oci_server_version', + 'oci_set_action', + 'oci_set_client_identifier', + 'oci_set_client_info', + 'oci_set_module_name', + 'oci_set_prefetch', + 'oci_statement_type', + 'oci_unregister_taf_callback', + 'odbc_autocommit', + 'odbc_close', + 'odbc_columnprivileges', + 'odbc_columns', + 'odbc_commit', + 'odbc_connect', + 'odbc_cursor', + 'odbc_data_source', + 'odbc_do', + 'odbc_error', + 'odbc_errormsg', + 'odbc_exec', + 'odbc_execute', + 'odbc_fetch_array', + 'odbc_fetch_into', + 'odbc_fetch_row', + 'odbc_field_len', + 'odbc_field_name', + 'odbc_field_num', + 'odbc_field_precision', + 'odbc_field_scale', + 'odbc_field_type', + 'odbc_foreignkeys', + 'odbc_free_result', + 'odbc_gettypeinfo', + 'odbc_next_result', + 'odbc_num_fields', + 'odbc_num_rows', + 'odbc_pconnect', + 'odbc_prepare', + 'odbc_primarykeys', + 'odbc_procedurecolumns', + 'odbc_procedures', + 'odbc_result', + 'odbc_result_all', + 'odbc_rollback', + 'odbc_setoption', + 'odbc_specialcolumns', + 'odbc_statistics', + 'odbc_tableprivileges', + 'odbc_tables', + 'openal_buffer_create', + 'openal_buffer_data', + 'openal_buffer_destroy', + 'openal_buffer_get', + 'openal_buffer_loadwav', + 'openal_context_create', + 'openal_context_current', + 'openal_context_destroy', + 'openal_context_process', + 'openal_context_suspend', + 'openal_device_close', + 'openal_device_open', + 'openal_source_create', + 'openal_source_destroy', + 'openal_source_get', + 'openal_source_pause', + 'openal_source_play', + 'openal_source_rewind', + 'openal_source_set', + 'openal_source_stop', + 'openal_stream', + 'opendir', + 'openssl_csr_new', + 'openssl_dh_compute_key', + 'openssl_free_key', + 'openssl_pkey_export', + 'openssl_pkey_free', + 'openssl_pkey_get_details', + 'openssl_spki_new', + 'openssl_x509_free', + 'pclose', + 'pfsockopen', + 'pg_affected_rows', + 'pg_cancel_query', + 'pg_client_encoding', + 'pg_close', + 'pg_connect_poll', + 'pg_connection_busy', + 'pg_connection_reset', + 'pg_connection_status', + 'pg_consume_input', + 'pg_convert', + 'pg_copy_from', + 'pg_copy_to', + 'pg_dbname', + 'pg_delete', + 'pg_end_copy', + 'pg_escape_bytea', + 'pg_escape_identifier', + 'pg_escape_literal', + 'pg_escape_string', + 'pg_execute', + 'pg_fetch_all', + 'pg_fetch_all_columns', + 'pg_fetch_array', + 'pg_fetch_assoc', + 'pg_fetch_row', + 'pg_field_name', + 'pg_field_num', + 'pg_field_size', + 'pg_field_table', + 'pg_field_type', + 'pg_field_type_oid', + 'pg_flush', + 'pg_free_result', + 'pg_get_notify', + 'pg_get_pid', + 'pg_get_result', + 'pg_host', + 'pg_insert', + 'pg_last_error', + 'pg_last_notice', + 'pg_last_oid', + 'pg_lo_close', + 'pg_lo_create', + 'pg_lo_export', + 'pg_lo_import', + 'pg_lo_open', + 'pg_lo_read', + 'pg_lo_read_all', + 'pg_lo_seek', + 'pg_lo_tell', + 'pg_lo_truncate', + 'pg_lo_unlink', + 'pg_lo_write', + 'pg_meta_data', + 'pg_num_fields', + 'pg_num_rows', + 'pg_options', + 'pg_parameter_status', + 'pg_ping', + 'pg_port', + 'pg_prepare', + 'pg_put_line', + 'pg_query', + 'pg_query_params', + 'pg_result_error', + 'pg_result_error_field', + 'pg_result_seek', + 'pg_result_status', + 'pg_select', + 'pg_send_execute', + 'pg_send_prepare', + 'pg_send_query', + 'pg_send_query_params', + 'pg_set_client_encoding', + 'pg_set_error_verbosity', + 'pg_socket', + 'pg_trace', + 'pg_transaction_status', + 'pg_tty', + 'pg_untrace', + 'pg_update', + 'pg_version', + 'php_user_filter::filter', + 'proc_close', + 'proc_get_status', + 'proc_terminate', + 'ps_add_bookmark', + 'ps_add_launchlink', + 'ps_add_locallink', + 'ps_add_note', + 'ps_add_pdflink', + 'ps_add_weblink', + 'ps_arc', + 'ps_arcn', + 'ps_begin_page', + 'ps_begin_pattern', + 'ps_begin_template', + 'ps_circle', + 'ps_clip', + 'ps_close', + 'ps_close_image', + 'ps_closepath', + 'ps_closepath_stroke', + 'ps_continue_text', + 'ps_curveto', + 'ps_delete', + 'ps_end_page', + 'ps_end_pattern', + 'ps_end_template', + 'ps_fill', + 'ps_fill_stroke', + 'ps_findfont', + 'ps_get_buffer', + 'ps_get_parameter', + 'ps_get_value', + 'ps_hyphenate', + 'ps_include_file', + 'ps_lineto', + 'ps_makespotcolor', + 'ps_moveto', + 'ps_new', + 'ps_open_file', + 'ps_open_image', + 'ps_open_image_file', + 'ps_open_memory_image', + 'ps_place_image', + 'ps_rect', + 'ps_restore', + 'ps_rotate', + 'ps_save', + 'ps_scale', + 'ps_set_border_color', + 'ps_set_border_dash', + 'ps_set_border_style', + 'ps_set_info', + 'ps_set_parameter', + 'ps_set_text_pos', + 'ps_set_value', + 'ps_setcolor', + 'ps_setdash', + 'ps_setflat', + 'ps_setfont', + 'ps_setgray', + 'ps_setlinecap', + 'ps_setlinejoin', + 'ps_setlinewidth', + 'ps_setmiterlimit', + 'ps_setoverprintmode', + 'ps_setpolydash', + 'ps_shading', + 'ps_shading_pattern', + 'ps_shfill', + 'ps_show', + 'ps_show2', + 'ps_show_boxed', + 'ps_show_xy', + 'ps_show_xy2', + 'ps_string_geometry', + 'ps_stringwidth', + 'ps_stroke', + 'ps_symbol', + 'ps_symbol_name', + 'ps_symbol_width', + 'ps_translate', + 'px_close', + 'px_create_fp', + 'px_date2string', + 'px_delete', + 'px_delete_record', + 'px_get_field', + 'px_get_info', + 'px_get_parameter', + 'px_get_record', + 'px_get_schema', + 'px_get_value', + 'px_insert_record', + 'px_new', + 'px_numfields', + 'px_numrecords', + 'px_open_fp', + 'px_put_record', + 'px_retrieve_record', + 'px_set_blob_file', + 'px_set_parameter', + 'px_set_tablename', + 'px_set_targetencoding', + 'px_set_value', + 'px_timestamp2string', + 'px_update_record', + 'radius_acct_open', + 'radius_add_server', + 'radius_auth_open', + 'radius_close', + 'radius_config', + 'radius_create_request', + 'radius_demangle', + 'radius_demangle_mppe_key', + 'radius_get_attr', + 'radius_put_addr', + 'radius_put_attr', + 'radius_put_int', + 'radius_put_string', + 'radius_put_vendor_addr', + 'radius_put_vendor_attr', + 'radius_put_vendor_int', + 'radius_put_vendor_string', + 'radius_request_authenticator', + 'radius_salt_encrypt_attr', + 'radius_send_request', + 'radius_server_secret', + 'radius_strerror', + 'readdir', + 'readfile', + 'recode_file', + 'rename', + 'rewind', + 'rewinddir', + 'rmdir', + 'rpm_close', + 'rpm_get_tag', + 'rpm_open', + 'sapi_windows_vt100_support', + 'scandir', + 'sem_acquire', + 'sem_get', + 'sem_release', + 'sem_remove', + 'set_file_buffer', + 'shm_attach', + 'shm_detach', + 'shm_get_var', + 'shm_has_var', + 'shm_put_var', + 'shm_remove', + 'shm_remove_var', + 'shmop_close', + 'shmop_delete', + 'shmop_open', + 'shmop_read', + 'shmop_size', + 'shmop_write', + 'socket_accept', + 'socket_addrinfo_bind', + 'socket_addrinfo_connect', + 'socket_addrinfo_explain', + 'socket_bind', + 'socket_clear_error', + 'socket_close', + 'socket_connect', + 'socket_export_stream', + 'socket_get_option', + 'socket_get_status', + 'socket_getopt', + 'socket_getpeername', + 'socket_getsockname', + 'socket_import_stream', + 'socket_last_error', + 'socket_listen', + 'socket_read', + 'socket_recv', + 'socket_recvfrom', + 'socket_recvmsg', + 'socket_send', + 'socket_sendmsg', + 'socket_sendto', + 'socket_set_block', + 'socket_set_blocking', + 'socket_set_nonblock', + 'socket_set_option', + 'socket_set_timeout', + 'socket_shutdown', + 'socket_write', + 'sqlite_close', + 'sqlite_fetch_string', + 'sqlite_has_more', + 'sqlite_open', + 'sqlite_popen', + 'sqlsrv_begin_transaction', + 'sqlsrv_cancel', + 'sqlsrv_client_info', + 'sqlsrv_close', + 'sqlsrv_commit', + 'sqlsrv_connect', + 'sqlsrv_execute', + 'sqlsrv_fetch', + 'sqlsrv_fetch_array', + 'sqlsrv_fetch_object', + 'sqlsrv_field_metadata', + 'sqlsrv_free_stmt', + 'sqlsrv_get_field', + 'sqlsrv_has_rows', + 'sqlsrv_next_result', + 'sqlsrv_num_fields', + 'sqlsrv_num_rows', + 'sqlsrv_prepare', + 'sqlsrv_query', + 'sqlsrv_rollback', + 'sqlsrv_rows_affected', + 'sqlsrv_send_stream_data', + 'sqlsrv_server_info', + 'ssh2_auth_agent', + 'ssh2_auth_hostbased_file', + 'ssh2_auth_none', + 'ssh2_auth_password', + 'ssh2_auth_pubkey_file', + 'ssh2_disconnect', + 'ssh2_exec', + 'ssh2_fetch_stream', + 'ssh2_fingerprint', + 'ssh2_methods_negotiated', + 'ssh2_publickey_add', + 'ssh2_publickey_init', + 'ssh2_publickey_list', + 'ssh2_publickey_remove', + 'ssh2_scp_recv', + 'ssh2_scp_send', + 'ssh2_sftp', + 'ssh2_sftp_chmod', + 'ssh2_sftp_lstat', + 'ssh2_sftp_mkdir', + 'ssh2_sftp_readlink', + 'ssh2_sftp_realpath', + 'ssh2_sftp_rename', + 'ssh2_sftp_rmdir', + 'ssh2_sftp_stat', + 'ssh2_sftp_symlink', + 'ssh2_sftp_unlink', + 'ssh2_shell', + 'ssh2_tunnel', + 'stomp_connect', + 'streamWrapper::stream_cast', + 'stream_bucket_append', + 'stream_bucket_make_writeable', + 'stream_bucket_new', + 'stream_bucket_prepend', + 'stream_context_create', + 'stream_context_get_default', + 'stream_context_get_options', + 'stream_context_get_params', + 'stream_context_set_default', + 'stream_context_set_params', + 'stream_copy_to_stream', + 'stream_encoding', + 'stream_filter_append', + 'stream_filter_prepend', + 'stream_filter_remove', + 'stream_get_contents', + 'stream_get_line', + 'stream_get_meta_data', + 'stream_isatty', + 'stream_set_blocking', + 'stream_set_chunk_size', + 'stream_set_read_buffer', + 'stream_set_timeout', + 'stream_set_write_buffer', + 'stream_socket_accept', + 'stream_socket_client', + 'stream_socket_enable_crypto', + 'stream_socket_get_name', + 'stream_socket_recvfrom', + 'stream_socket_sendto', + 'stream_socket_server', + 'stream_socket_shutdown', + 'stream_supports_lock', + 'svn_fs_abort_txn', + 'svn_fs_apply_text', + 'svn_fs_begin_txn2', + 'svn_fs_change_node_prop', + 'svn_fs_check_path', + 'svn_fs_contents_changed', + 'svn_fs_copy', + 'svn_fs_delete', + 'svn_fs_dir_entries', + 'svn_fs_file_contents', + 'svn_fs_file_length', + 'svn_fs_is_dir', + 'svn_fs_is_file', + 'svn_fs_make_dir', + 'svn_fs_make_file', + 'svn_fs_node_created_rev', + 'svn_fs_node_prop', + 'svn_fs_props_changed', + 'svn_fs_revision_prop', + 'svn_fs_revision_root', + 'svn_fs_txn_root', + 'svn_fs_youngest_rev', + 'svn_repos_create', + 'svn_repos_fs', + 'svn_repos_fs_begin_txn_for_commit', + 'svn_repos_fs_commit_txn', + 'svn_repos_open', + 'sybase_affected_rows', + 'sybase_close', + 'sybase_connect', + 'sybase_data_seek', + 'sybase_fetch_array', + 'sybase_fetch_assoc', + 'sybase_fetch_field', + 'sybase_fetch_object', + 'sybase_fetch_row', + 'sybase_field_seek', + 'sybase_free_result', + 'sybase_num_fields', + 'sybase_num_rows', + 'sybase_pconnect', + 'sybase_query', + 'sybase_result', + 'sybase_select_db', + 'sybase_set_message_handler', + 'sybase_unbuffered_query', + 'tmpfile', + 'udm_add_search_limit', + 'udm_alloc_agent', + 'udm_alloc_agent_array', + 'udm_cat_list', + 'udm_cat_path', + 'udm_check_charset', + 'udm_clear_search_limits', + 'udm_crc32', + 'udm_errno', + 'udm_error', + 'udm_find', + 'udm_free_agent', + 'udm_free_res', + 'udm_get_doc_count', + 'udm_get_res_field', + 'udm_get_res_param', + 'udm_hash32', + 'udm_load_ispell_data', + 'udm_set_agent_param', + 'unlink', + 'vfprintf', + 'w32api_init_dtype', + 'wddx_add_vars', + 'wddx_packet_end', + 'wddx_packet_start', + 'xml_get_current_byte_index', + 'xml_get_current_column_number', + 'xml_get_current_line_number', + 'xml_get_error_code', + 'xml_parse', + 'xml_parse_into_struct', + 'xml_parser_create', + 'xml_parser_create_ns', + 'xml_parser_free', + 'xml_parser_get_option', + 'xml_parser_set_option', + 'xml_set_character_data_handler', + 'xml_set_default_handler', + 'xml_set_element_handler', + 'xml_set_end_namespace_decl_handler', + 'xml_set_external_entity_ref_handler', + 'xml_set_notation_decl_handler', + 'xml_set_object', + 'xml_set_processing_instruction_handler', + 'xml_set_start_namespace_decl_handler', + 'xml_set_unparsed_entity_decl_handler', + 'xmlrpc_server_add_introspection_data', + 'xmlrpc_server_call_method', + 'xmlrpc_server_create', + 'xmlrpc_server_destroy', + 'xmlrpc_server_register_introspection_callback', + 'xmlrpc_server_register_method', + 'xmlwriter_end_attribute', + 'xmlwriter_end_cdata', + 'xmlwriter_end_comment', + 'xmlwriter_end_document', + 'xmlwriter_end_dtd', + 'xmlwriter_end_dtd_attlist', + 'xmlwriter_end_dtd_element', + 'xmlwriter_end_dtd_entity', + 'xmlwriter_end_element', + 'xmlwriter_end_pi', + 'xmlwriter_flush', + 'xmlwriter_full_end_element', + 'xmlwriter_open_memory', + 'xmlwriter_open_uri', + 'xmlwriter_output_memory', + 'xmlwriter_set_indent', + 'xmlwriter_set_indent_string', + 'xmlwriter_start_attribute', + 'xmlwriter_start_attribute_ns', + 'xmlwriter_start_cdata', + 'xmlwriter_start_comment', + 'xmlwriter_start_document', + 'xmlwriter_start_dtd', + 'xmlwriter_start_dtd_attlist', + 'xmlwriter_start_dtd_element', + 'xmlwriter_start_dtd_entity', + 'xmlwriter_start_element', + 'xmlwriter_start_element_ns', + 'xmlwriter_start_pi', + 'xmlwriter_text', + 'xmlwriter_write_attribute', + 'xmlwriter_write_attribute_ns', + 'xmlwriter_write_cdata', + 'xmlwriter_write_comment', + 'xmlwriter_write_dtd', + 'xmlwriter_write_dtd_attlist', + 'xmlwriter_write_dtd_element', + 'xmlwriter_write_dtd_entity', + 'xmlwriter_write_element', + 'xmlwriter_write_element_ns', + 'xmlwriter_write_pi', + 'xmlwriter_write_raw', + 'xslt_create', + 'yaz_addinfo', + 'yaz_ccl_conf', + 'yaz_ccl_parse', + 'yaz_close', + 'yaz_database', + 'yaz_element', + 'yaz_errno', + 'yaz_error', + 'yaz_es', + 'yaz_es_result', + 'yaz_get_option', + 'yaz_hits', + 'yaz_itemorder', + 'yaz_present', + 'yaz_range', + 'yaz_record', + 'yaz_scan', + 'yaz_scan_result', + 'yaz_schema', + 'yaz_search', + 'yaz_sort', + 'yaz_syntax', + 'zip_close', + 'zip_entry_close', + 'zip_entry_compressedsize', + 'zip_entry_compressionmethod', + 'zip_entry_filesize', + 'zip_entry_name', + 'zip_entry_open', + 'zip_entry_read', + 'zip_open', + 'zip_read', + ]; + } +} +Resource Operations + +Copyright (c) 2015-2018, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +��#cW�g3k���� =��GBMB \ No newline at end of file diff --git a/composer.json b/composer.json index df338583..981d5007 100644 --- a/composer.json +++ b/composer.json @@ -54,11 +54,11 @@ "repositories": [ { "type": "git", - "url": "git@github.com:swoft-cloud/swoft-component.git" + "url": "https://github.com/swoft-cloud/swoft-component.git" }, { "type": "composer", - "url": "https://packagist.laravel-china.org/" + "url": "https://packagist.phpcomposer.com/" } ] } \ No newline at end of file From 636550ef0989798d8eb6b4876746ef7b6586ae9a Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 3 Mar 2019 00:25:43 +0800 Subject: [PATCH 297/643] add some example folders --- app/Rpc/Middleware/.keep | 0 app/WebSocket/Chat/ChatController.php | 31 +++++++++++++++++++++++++++ app/WebSocket/ChatModule.php | 25 +++++++++++++++++++++ app/WebSocket/Middleware/.keep | 0 resource/languages/.keep | 0 resource/views/.keep | 0 test/bootstrap.php | 3 +++ 7 files changed, 59 insertions(+) create mode 100644 app/Rpc/Middleware/.keep create mode 100644 app/WebSocket/Chat/ChatController.php create mode 100644 app/WebSocket/ChatModule.php create mode 100644 app/WebSocket/Middleware/.keep create mode 100644 resource/languages/.keep create mode 100644 resource/views/.keep create mode 100644 test/bootstrap.php diff --git a/app/Rpc/Middleware/.keep b/app/Rpc/Middleware/.keep new file mode 100644 index 00000000..e69de29b diff --git a/app/WebSocket/Chat/ChatController.php b/app/WebSocket/Chat/ChatController.php new file mode 100644 index 00000000..55f4fd3a --- /dev/null +++ b/app/WebSocket/Chat/ChatController.php @@ -0,0 +1,31 @@ + Date: Wed, 6 Mar 2019 14:57:45 +0800 Subject: [PATCH 298/643] fix --- app/WebSocket/ChatModule.php | 27 ++++++++++++++++++++------- bin/swoft | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index bd4b5a95..917c4c03 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -4,22 +4,35 @@ use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; use Swoft\WebSocket\Server\Contract\WsModuleInterface; -use Swoole\Http\ServerRequest; +use Swoole\Http\Request; use Swoole\Http\Response; -use Swoole\WebSocket\Frame; -use Swoole\WebSocket\Server; use Swoft\WebSocket\Server\MessageParser\JsonParser; -use Swoft\WebSocket\Server\Annotation\Mapping\OnClose; -use Swoft\WebSocket\Server\Annotation\Mapping\OnHandShake; -use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen; /** * Class AbstractModule * @since 2.0 * - * @WsModule(path="/chat", messageParser=JsonParser::class) + * @WsModule(name="chat", path="/chat", messageParser=JsonParser::class) */ class ChatModule implements WsModuleInterface { // + /** + * 在这里你可以验证握手的请求信息 + * - 必须返回含有两个元素的array + * - 第一个元素的值来决定是否进行握手 + * - 第二个元素是response对象 + * - 可以在response设置一些自定义header,body等信息 + * @param Request $request + * @param Response $response + * @return array + * [ + * self::HANDSHAKE_OK, + * $response + * ] + */ + public function checkHandshake(Request $request, Response $response): array + { + return [self::ACCEPT, $response]; + } } diff --git a/bin/swoft b/bin/swoft index c2db65b6..7a57b1c2 100644 --- a/bin/swoft +++ b/bin/swoft @@ -1,7 +1,7 @@ #!/usr/bin/env php Date: Sat, 16 Mar 2019 18:11:09 +0800 Subject: [PATCH 299/643] add --- app/Aspect/TestLog.php | 23 ----------------- app/Controller/TestController.php | 43 +++++++++++++++++++++++++++++-- bin/swoft | 1 + bin/test.php | 8 ++++++ run | 12 +++++++++ 5 files changed, 62 insertions(+), 25 deletions(-) delete mode 100644 app/Aspect/TestLog.php create mode 100644 bin/test.php create mode 100644 run diff --git a/app/Aspect/TestLog.php b/app/Aspect/TestLog.php deleted file mode 100644 index 49e85922..00000000 --- a/app/Aspect/TestLog.php +++ /dev/null @@ -1,23 +0,0 @@ -run(); \ No newline at end of file diff --git a/bin/test.php b/bin/test.php new file mode 100644 index 00000000..bebe5fcc --- /dev/null +++ b/bin/test.php @@ -0,0 +1,8 @@ +on('request', function ($request, $response) { + $response->end("

    Hello Swoole. #".rand(1000, 9999)."

    "); +}); +$http->start(); \ No newline at end of file diff --git a/run b/run new file mode 100644 index 00000000..8afe95ce --- /dev/null +++ b/run @@ -0,0 +1,12 @@ +GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7 +Copyright (C) 2013 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. Type "show copying" +and "show warranty" for details. +This GDB was configured as "x86_64-redhat-linux-gnu". +For bug reporting instructions, please see: +... +Reading symbols from /usr/local/php/bin/php...done. +[?1034h(gdb) ^C(gdb) ^C(gdb) q quti +(gdb) ^C(gdb) ^C(gdb) ^C(gdb) ^C(gdb) ^C(gdb) ^Z \ No newline at end of file From 6a3976a4147a3e766d8f0b02378810a39834451d Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 21 Mar 2019 15:53:23 +0800 Subject: [PATCH 300/643] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ffa20442..df36edab 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,6 @@ "swoft/cache": "^1.0", "swoft/redis": "^1.0", "swoft/console": "^1.0", - "swoft/devtool": "^1.0", "swoft/session": "^1.0", "swoft/i18n": "^1.0", "swoft/process": "^1.0", @@ -34,6 +33,7 @@ }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", + "swoft/devtool": "^1.0", "phpunit/phpunit": "^5.7", "friendsofphp/php-cs-fixer": "^2.10", "psy/psysh": "@stable" From 43cfe4a16585473acbed01cf9bb3434e690175e3 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 24 Mar 2019 18:46:03 +0800 Subject: [PATCH 301/643] add demo --- app/Controller/TestController.php | 81 +++++++++++++++++++++++++++- app/Model/Entity/User.php | 88 +++++++++++++++++++++++++++++++ bin/test.php | 38 ++++++++++++- 3 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 app/Model/Entity/User.php diff --git a/app/Controller/TestController.php b/app/Controller/TestController.php index d4d076c6..bf76bcf9 100644 --- a/app/Controller/TestController.php +++ b/app/Controller/TestController.php @@ -2,10 +2,15 @@ namespace App\Controller; +use App\Model\Entity\User; +use function foo\func; +use Swoft\Db\DB; +use Swoft\Db\DbEvent; use Swoft\Http\Message\Request; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoole\Coroutine; /** * Class TestController @@ -24,9 +29,83 @@ class TestController */ public function test(): string { - return 'swoft framework hello'; + $user = User::find(22); + sgo(function () { +// $user = User::find(22); + User::where('id', '=', 22); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="ts") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function ts() + { + DB::pool()->beginTransaction(); + $user = User::find(22); + + \sgo(function (){ + DB::pool()->beginTransaction(); + $user = User::find(22); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="cm") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function cm() + { + DB::pool()->beginTransaction(); + $user = User::find(22); + DB::pool()->commit(); + + \sgo(function (){ + DB::pool()->beginTransaction(); + $user = User::find(22); + DB::pool()->commit(); + }); + + return json_encode($user->toArray()); } + /** + * @RequestMapping(route="rl") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function rl() + { + DB::pool()->beginTransaction(); + $user = User::find(22); + DB::pool()->rollBack(); + + \sgo(function (){ + DB::pool()->beginTransaction(); + $user = User::find(22); + DB::pool()->rollBack(); + }); + + return json_encode($user->toArray()); + } + + /** * @param Response $response * @param Request $request diff --git a/app/Model/Entity/User.php b/app/Model/Entity/User.php new file mode 100644 index 00000000..4177aaeb --- /dev/null +++ b/app/Model/Entity/User.php @@ -0,0 +1,88 @@ +id; + } + + /** + * @param int|null $id + */ + public function setId(?int $id): void + { + $this->id = $id; + } + + /** + * @return int|null + */ + public function getAge(): ?int + { + return $this->age; + } + + /** + * @param int|null $age + */ + public function setAge(?int $age): void + { + $this->age = $age; + } + + /** + * @return string|null + */ + public function getUserDesc(): ?string + { + return $this->userDesc; + } + + /** + * @param string|null $userDesc + */ + public function setUserDesc(?string $userDesc): void + { + $this->userDesc = $userDesc; + } +} \ No newline at end of file diff --git a/bin/test.php b/bin/test.php index bebe5fcc..449cbd81 100644 --- a/bin/test.php +++ b/bin/test.php @@ -3,6 +3,40 @@ $http = new Swoole\Http\Server("0.0.0.0", 88); $http->on('request', function ($request, $response) { - $response->end("

    Hello Swoole. #".rand(1000, 9999)."

    "); + $pdo = new \PDO('mysql:dbname=test;host=172.17.0.2', 'root', 'swoft123456'); + + for ($i = 0; $i < 30000; $i++) { + $pdo->beginTransaction(); + $stmt = $pdo->prepare('select * from `user` where `user`.`id` = 22 limit 1'); + $stmt->execute(); + $stmt->setFetchMode(\PDO::FETCH_OBJ); + $result = $stmt->fetchAll(); + +// var_dump($result); + + $pdo->rollBack(); + } + + + $response->end("

    Hello Swoole. #" . rand(1000, 9999) . "

    "); }); -$http->start(); \ No newline at end of file +$http->start(); + +class Pool +{ + private static $pdo; + + /** + * @return PDO + */ + public static function getPdo(): \PDO + { + if (!empty(self::$pdo)) { + return self::$pdo; + } + + self::$pdo = new \PDO('mysql:dbname=test;host=172.17.0.2', 'root', 'swoft123456'); + + return self::$pdo; + } +} \ No newline at end of file From 721e266d12bc7622e3e5de9813445dce2beedf88 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 25 Mar 2019 17:04:25 +0800 Subject: [PATCH 302/643] modify entity --- app/Model/Entity/User.php | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/app/Model/Entity/User.php b/app/Model/Entity/User.php index 4177aaeb..29996b09 100644 --- a/app/Model/Entity/User.php +++ b/app/Model/Entity/User.php @@ -20,23 +20,36 @@ class User extends Model { /** * @Id() + * * @Column(name="id", prop="id") * @var int|null */ - public $id; + private $id; + + /** + * @Column() + * @var string|null + */ + private $name; + + /** + * @Column(name="password", hidden=true) + * @var string|null + */ + private $pwd; /** * @Column() * * @var int|null */ - public $age; + private $age; /** * @Column(name="user_desc", prop="udesc") * @var string|null */ - public $userDesc; + private $userDesc; /** * @return int|null @@ -70,6 +83,38 @@ public function setAge(?int $age): void $this->age = $age; } + /** + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * @param string|null $name + */ + public function setName(?string $name): void + { + $this->name = $name; + } + + /** + * @return string|null + */ + public function getPwd(): ?string + { + return $this->pwd; + } + + /** + * @param string|null $pwd + */ + public function setPwd(?string $pwd): void + { + $this->pwd = $pwd; + } + /** * @return string|null */ From c5d48235d83cde94aa0dfacb8cad224850dc0963 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 26 Mar 2019 10:18:39 +0800 Subject: [PATCH 303/643] modify composer --- composer.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composer.json b/composer.json index 981d5007..256a9bc0 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,10 @@ "swoft/connection-pool": "^2.0", "swoft/test": "^2.0", "swoft/console": "^2.0", + "swoft/rpc": "^2.0", + "swoft/rpc-server": "^2.0", + "swoft/rpc-client": "^2.0", + "swoft/task": "^2.0", "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { From ead4390519e08f25d4587155531ccb965c8443ac Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 26 Mar 2019 21:47:54 +0800 Subject: [PATCH 304/643] add rpc --- app/Rpc/Controller/RpcController.php | 35 +++++++++++++++ app/Rpc/Lib/UserInterface.php | 38 +++++++++++++++++ app/Rpc/Service/UserService.php | 64 ++++++++++++++++++++++++++++ app/WebSocket/ChatModule.php | 28 +++++------- composer.json | 3 +- 5 files changed, 150 insertions(+), 18 deletions(-) create mode 100644 app/Rpc/Controller/RpcController.php create mode 100644 app/Rpc/Lib/UserInterface.php create mode 100644 app/Rpc/Service/UserService.php diff --git a/app/Rpc/Controller/RpcController.php b/app/Rpc/Controller/RpcController.php new file mode 100644 index 00000000..6748f49f --- /dev/null +++ b/app/Rpc/Controller/RpcController.php @@ -0,0 +1,35 @@ +userService->getUsers([1, 2]); + } +} \ No newline at end of file diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php new file mode 100644 index 00000000..763cfd04 --- /dev/null +++ b/app/Rpc/Lib/UserInterface.php @@ -0,0 +1,38 @@ + $id, + 'name' => 'name' + ]; + } + + /** + * @param int $id + * + * @return bool + */ + public function isExist(int $id): bool + { + return $id == 1; + } + + /** + * @param string $name + * + * @return int + */ + public function getByName(string $name): int + { + return 18306; + } + + /** + * @param array $ids + * + * @return array + */ + public function getUsers(array $ids): array + { + $users = []; + foreach ($ids as $id) { + $users['id'] = $id; + $users['name'] = 'name' . $id; + } + + return $users; + } +} \ No newline at end of file diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index 917c4c03..e370fe44 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -7,6 +7,7 @@ use Swoole\Http\Request; use Swoole\Http\Response; use Swoft\WebSocket\Server\MessageParser\JsonParser; +use Swoole\WebSocket\Server; /** * Class AbstractModule @@ -16,23 +17,16 @@ */ class ChatModule implements WsModuleInterface { - // - /** - * 在这里你可以验证握手的请求信息 - * - 必须返回含有两个元素的array - * - 第一个元素的值来决定是否进行握手 - * - 第二个元素是response对象 - * - 可以在response设置一些自定义header,body等信息 - * @param Request $request - * @param Response $response - * @return array - * [ - * self::HANDSHAKE_OK, - * $response - * ] - */ - public function checkHandshake(Request $request, Response $response): array + public function checkHandshake(\Swoft\Http\Message\Request $request, \Swoft\Http\Message\Response $response): array + { + return []; + } + + public function onOpen(Server $server, \Swoft\Http\Message\Request $request, int $fd): void + { + } + + public function onClose(Server $server, int $fd): void { - return [self::ACCEPT, $response]; } } diff --git a/composer.json b/composer.json index 256a9bc0..1d07fc50 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "swoft/rpc-server": "^2.0", "swoft/rpc-client": "^2.0", "swoft/task": "^2.0", + "swoft/proxy": "^2.0", "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { @@ -62,7 +63,7 @@ }, { "type": "composer", - "url": "https://packagist.phpcomposer.com/" + "url": "https://packagist.laravel-china.org/" } ] } \ No newline at end of file From efdf0ba52624c0e406714d01bf6e95ae2e4eafb5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 29 Mar 2019 02:07:30 +0800 Subject: [PATCH 305/643] add rpc --- app/Rpc/Controller/RpcController.php | 6 ++++-- app/bean.php | 29 +++++++++++++++++++++++++++- config/rpc.php | 3 +++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 config/rpc.php diff --git a/app/Rpc/Controller/RpcController.php b/app/Rpc/Controller/RpcController.php index 6748f49f..63f59469 100644 --- a/app/Rpc/Controller/RpcController.php +++ b/app/Rpc/Controller/RpcController.php @@ -19,14 +19,16 @@ class RpcController { /** - * @Reference(pool="user") + * @Reference(pool="user.pool") + * * @var UserInterface */ private $userService; /** * @RequestMapping("user") - * @return string + * + * @return array */ public function user(): array { diff --git a/app/bean.php b/app/bean.php index 3aa9a161..5b13abce 100644 --- a/app/bean.php +++ b/app/bean.php @@ -7,5 +7,32 @@ '${App\Model\Data\DemoData}' ], 'definitionData' => 'definitionData...' - ] + ], + + 'httpServer' => [ + 'class' => \Swoft\Http\Server\HttpServer::class, + 'port' => 88, + 'listener' => [ + 'rpc' => \bean('rpcServer') + ] + ], + 'user' => [ + 'class' => \Swoft\Rpc\Client\Client::class, + 'host' => '127.0.0.1', + 'port' => 18307, + 'setting' => [ + 'timeout' => 0.5, + 'connect_timeout' => 1.0, + 'write_timeout' => 10.0, + 'read_timeout' => 0.5, + ], + 'packet' => \bean('clientPacket') + ], + 'user.pool' => [ + 'class' => \Swoft\Rpc\Client\Pool::class, + 'client' => bean('user') + ], + 'rpcServer' => [ + 'class' => \Swoft\Rpc\Server\ServiceServer::class, + ], ]; \ No newline at end of file diff --git a/config/rpc.php b/config/rpc.php new file mode 100644 index 00000000..60c0351d --- /dev/null +++ b/config/rpc.php @@ -0,0 +1,3 @@ + Date: Tue, 2 Apr 2019 15:29:58 +0800 Subject: [PATCH 306/643] fix bug --- app/bean.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bean.php b/app/bean.php index 5b13abce..64fb5df2 100644 --- a/app/bean.php +++ b/app/bean.php @@ -26,7 +26,7 @@ 'write_timeout' => 10.0, 'read_timeout' => 0.5, ], - 'packet' => \bean('clientPacket') + 'packet' => \bean('rpcClientPacket') ], 'user.pool' => [ 'class' => \Swoft\Rpc\Client\Pool::class, From 02b4db1b356cd0b9cb5e32e5171e8adc6e67b839 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 2 Apr 2019 15:35:30 +0800 Subject: [PATCH 307/643] add --- app/WebSocket/ChatModule.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index e370fe44..4130d038 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -17,6 +17,11 @@ */ class ChatModule implements WsModuleInterface { + public function onError(\Throwable $e, int $fd): void + { + + } + public function checkHandshake(\Swoft\Http\Message\Request $request, \Swoft\Http\Message\Response $response): array { return []; From f7e39096b377079f29ec3cc1e8d0bc8374c05b69 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 3 Apr 2019 12:02:45 +0800 Subject: [PATCH 308/643] add log --- app/bean.php | 4 ++++ config/rpc.php | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 config/rpc.php diff --git a/app/bean.php b/app/bean.php index 64fb5df2..b9333f2d 100644 --- a/app/bean.php +++ b/app/bean.php @@ -9,6 +9,10 @@ 'definitionData' => 'definitionData...' ], + 'logger' => [ + 'flushRequest' => true, + 'enable' => true, + ], 'httpServer' => [ 'class' => \Swoft\Http\Server\HttpServer::class, 'port' => 88, diff --git a/config/rpc.php b/config/rpc.php deleted file mode 100644 index 60c0351d..00000000 --- a/config/rpc.php +++ /dev/null @@ -1,3 +0,0 @@ - Date: Wed, 3 Apr 2019 21:05:32 +0800 Subject: [PATCH 309/643] add rpc --- app/Rpc/Controller/RpcController.php | 41 ++++++++++++++++- app/Rpc/Lib/UserInterface.php | 2 - app/Rpc/Service/UserService.php | 8 +++- app/Rpc/Service/UserService2.php | 69 ++++++++++++++++++++++++++++ app/bean.php | 4 +- 5 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 app/Rpc/Service/UserService2.php diff --git a/app/Rpc/Controller/RpcController.php b/app/Rpc/Controller/RpcController.php index 63f59469..dbcc6978 100644 --- a/app/Rpc/Controller/RpcController.php +++ b/app/Rpc/Controller/RpcController.php @@ -25,6 +25,13 @@ class RpcController */ private $userService; + /** + * @Reference(pool="user.pool", version="1.1") + * + * @var UserInterface + */ + private $userService2; + /** * @RequestMapping("user") * @@ -32,6 +39,38 @@ class RpcController */ public function user(): array { - return $this->userService->getUsers([1, 2]); + $users = $this->userService->getUsers([12, 16]); + $user = $this->userService->getUser(12); + $user2 = $this->userService->getByName('name'); + $bool = $this->userService->isExist(12); + + $data = [ + $users, + $user, + $user2, + $bool, + ]; + return $data; + } + + /** + * @RequestMapping("user2") + * + * @return array + */ + public function user2(): array + { + $users = $this->userService2->getUsers([12, 16]); + $user = $this->userService2->getUser(12); + $user2 = $this->userService2->getByName('name'); + $bool = $this->userService2->isExist(12); + + $data = [ + $users, + $user, + $user2, + $bool, + ]; + return $data; } } \ No newline at end of file diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index 763cfd04..8869f45e 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -4,8 +4,6 @@ namespace App\Rpc\Lib; -use Swoft\Rpc\Client\Concern\ServiceTrait; - interface UserInterface { /** diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index f50183b0..a1abf9a4 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -5,11 +5,14 @@ use App\Rpc\Lib\UserInterface; +use Swoft\Rpc\Server\Annotation\Mapping\Service; /** * Class UserService * * @since 2.0 + * + * @Service() */ class UserService implements UserInterface { @@ -55,8 +58,9 @@ public function getUsers(array $ids): array { $users = []; foreach ($ids as $id) { - $users['id'] = $id; - $users['name'] = 'name' . $id; + $user['id'] = $id; + $user['name'] = 'name' . $id; + $users[] = $user; } return $users; diff --git a/app/Rpc/Service/UserService2.php b/app/Rpc/Service/UserService2.php new file mode 100644 index 00000000..408e9b03 --- /dev/null +++ b/app/Rpc/Service/UserService2.php @@ -0,0 +1,69 @@ + '1.1', + 'id' => $id, + 'name' => 'name' + ]; + } + + /** + * @param int $id + * + * @return bool + */ + public function isExist(int $id): bool + { + return $id == 1; + } + + /** + * @param string $name + * + * @return int + */ + public function getByName(string $name): int + { + return 18306 + 10000; + } + + /** + * @param array $ids + * + * @return array + */ + public function getUsers(array $ids): array + { + $users = []; + foreach ($ids as $id) { + $users['id'] = $id; + $users['v'] = '1.1'; + $users['name'] = 'name' . $id; + } + + return $users; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index b9333f2d..0f781549 100644 --- a/app/bean.php +++ b/app/bean.php @@ -10,8 +10,8 @@ ], 'logger' => [ - 'flushRequest' => true, - 'enable' => true, + 'flushRequest' => false, + 'enable' => false, ], 'httpServer' => [ 'class' => \Swoft\Http\Server\HttpServer::class, From bce2e9baa99057a292aa8959a66cf3410e180d15 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 7 Apr 2019 22:24:52 +0800 Subject: [PATCH 310/643] add task --- app/Task/Controller/TaskController.php | 32 ++++++++++++++++++++++++++ app/Task/TestTask.php | 25 ++++++++++++++++++++ app/bean.php | 10 +++++++- 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 app/Task/Controller/TaskController.php create mode 100644 app/Task/TestTask.php diff --git a/app/Task/Controller/TaskController.php b/app/Task/Controller/TaskController.php new file mode 100644 index 00000000..ba503ff1 --- /dev/null +++ b/app/Task/Controller/TaskController.php @@ -0,0 +1,32 @@ + 'definitionData...' ], - 'logger' => [ + 'logger' => [ 'flushRequest' => false, 'enable' => false, ], @@ -18,6 +18,14 @@ 'port' => 88, 'listener' => [ 'rpc' => \bean('rpcServer') + ], + 'on' => [ + \Swoft\Server\Swoole\SwooleEvent::TASK => \bean(\Swoft\Task\Swoole\TaskListener::class), + \Swoft\Server\Swoole\SwooleEvent::FINISH => \bean(\Swoft\Task\Swoole\FinishListener::class) + ], + 'setting' => [ + 'task_worker_num' => 1, + 'task_enable_coroutine' => true ] ], 'user' => [ From 5ff7e6552c932f9346dd6553a3b733addef54f9f Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 8 Apr 2019 15:20:50 +0800 Subject: [PATCH 311/643] Fixed task bug --- app/Task/Controller/TaskController.php | 7 +-- bin/test.php | 71 +++++++++++++------------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/app/Task/Controller/TaskController.php b/app/Task/Controller/TaskController.php index ba503ff1..40aaaf4f 100644 --- a/app/Task/Controller/TaskController.php +++ b/app/Task/Controller/TaskController.php @@ -25,8 +25,9 @@ class TaskController */ public function co(): array { - $result = Task::co('test', 'getData', [], 10); -// $result = Task::async('test', 'getData', []); - return $result; +// $result = Task::co('test', 'getData', [], 10); + $result = Task::async('test', 'getData', []); + var_dump($result); + return [$result]; } } \ No newline at end of file diff --git a/bin/test.php b/bin/test.php index 449cbd81..009c253d 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,42 +1,43 @@ set(array( + 'worker_num' => 1, + 'task_worker_num' => 4, + 'task_enable_coroutine' => true, +)); + +$serv->on('request', function ($request, $response) use ($serv) { +// $task_id = $serv->task('stelins', -1); + $task_id = $serv->task('stelins', -1, null); +var_dump($task_id); + $response->end("

    Hello Swoole. #" . rand(1000, 9999) . "

    "); +}); -$http = new Swoole\Http\Server("0.0.0.0", 88); -$http->on('request', function ($request, $response) { - $pdo = new \PDO('mysql:dbname=test;host=172.17.0.2', 'root', 'swoft123456'); +$serv->on('Task', function ($serv, Swoole\Server\Task $task) { + //来自哪个`Worker`进程 + $task->worker_id; + //任务的编号 + $task->id; + //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags + $task->flags; + //任务的数据 + $task->data; + //协程 API + co::sleep(0.2); + //完成任务,结束并返回数据 + $task->finish([123, 'hello']); +}); - for ($i = 0; $i < 30000; $i++) { - $pdo->beginTransaction(); - $stmt = $pdo->prepare('select * from `user` where `user`.`id` = 22 limit 1'); - $stmt->execute(); - $stmt->setFetchMode(\PDO::FETCH_OBJ); - $result = $stmt->fetchAll(); +$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { + var_dump($data, $task_id); +// echo "Task#$task_id finished, data_len=" . strlen($data) . PHP_EOL; +}); -// var_dump($result); +$serv->on('workerStart', function ($serv, $worker_id) { - $pdo->rollBack(); - } +}); +$serv->start(); - $response->end("

    Hello Swoole. #" . rand(1000, 9999) . "

    "); -}); -$http->start(); - -class Pool -{ - private static $pdo; - - /** - * @return PDO - */ - public static function getPdo(): \PDO - { - if (!empty(self::$pdo)) { - return self::$pdo; - } - - self::$pdo = new \PDO('mysql:dbname=test;host=172.17.0.2', 'root', 'swoft123456'); - - return self::$pdo; - } -} \ No newline at end of file From 17bd397f2ed66a6e9e52a0ecd9e52c2e37aaa69c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 9 Apr 2019 19:35:56 +0800 Subject: [PATCH 312/643] Added rediss --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1d07fc50..3ead83d7 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "swoft/rpc-server": "^2.0", "swoft/rpc-client": "^2.0", "swoft/task": "^2.0", + "swoft/redis": "^2.0", "swoft/proxy": "^2.0", "swoft/component": "2.0.x-dev as 2.0" }, From 37040b32ddbd81e4db4e53702851a7a12bd6c646 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 12 Apr 2019 17:32:17 +0800 Subject: [PATCH 313/643] Added redis demo --- app/Controller/TestController.php | 20 ++++++++++-- bin/test.php | 54 ++++++++++--------------------- composer.json | 1 + 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/app/Controller/TestController.php b/app/Controller/TestController.php index bf76bcf9..3238e324 100644 --- a/app/Controller/TestController.php +++ b/app/Controller/TestController.php @@ -10,6 +10,7 @@ use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\Redis\Redis; use Swoole\Coroutine; /** @@ -51,7 +52,7 @@ public function ts() DB::pool()->beginTransaction(); $user = User::find(22); - \sgo(function (){ + \sgo(function () { DB::pool()->beginTransaction(); $user = User::find(22); }); @@ -73,7 +74,7 @@ public function cm() $user = User::find(22); DB::pool()->commit(); - \sgo(function (){ + \sgo(function () { DB::pool()->beginTransaction(); $user = User::find(22); DB::pool()->commit(); @@ -96,7 +97,7 @@ public function rl() $user = User::find(22); DB::pool()->rollBack(); - \sgo(function (){ + \sgo(function () { DB::pool()->beginTransaction(); $user = User::find(22); DB::pool()->rollBack(); @@ -134,4 +135,17 @@ public function user(int $uid, Response $response, string $name, int $age, int $ { return 'uid=' . $uid . ' name=' . $name . ' age=' . $age . ' count=' . $count . ' response=' . get_class($response); } + + /** + * @RequestMapping(route="redis") + * + * @return array + */ + public function redis(): array + { + Redis::set('a', 'b'); + $a = Redis::get('a'); + + return [$a]; + } } \ No newline at end of file diff --git a/bin/test.php b/bin/test.php index 009c253d..bef2d90e 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,43 +1,23 @@ set(array( - 'worker_num' => 1, - 'task_worker_num' => 4, - 'task_enable_coroutine' => true, -)); +$cms = []; +foreach ($r->getMethods() as $method) { + $cms[] = strtolower($method->getName()); +} -$serv->on('request', function ($request, $response) use ($serv) { -// $task_id = $serv->task('stelins', -1); - $task_id = $serv->task('stelins', -1, null); -var_dump($task_id); - $response->end("

    Hello Swoole. #" . rand(1000, 9999) . "

    "); -}); +//var_dump($cms); -$serv->on('Task', function ($serv, Swoole\Server\Task $task) { - //来自哪个`Worker`进程 - $task->worker_id; - //任务的编号 - $task->id; - //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags - $task->flags; - //任务的数据 - $task->data; - //协程 API - co::sleep(0.2); - //完成任务,结束并返回数据 - $task->finish([123, 'hello']); -}); +$rc = new \ReflectionClass(\RedisCluster::class); -$serv->on('Finish', function (swoole_server $serv, $task_id, $data) { - var_dump($data, $task_id); -// echo "Task#$task_id finished, data_len=" . strlen($data) . PHP_EOL; -}); - -$serv->on('workerStart', function ($serv, $worker_id) { - -}); - -$serv->start(); +$rcs = []; +foreach ($rc->getMethods() as $method) { + $rcs[] = strtolower($method->getName()); +} +//var_dump(count($rcs)); +echo '['; +foreach (array_intersect($cms, $rcs) as $a){ + echo "'$a',".PHP_EOL; +} +echo ']'; \ No newline at end of file diff --git a/composer.json b/composer.json index 3ead83d7..edf24c9d 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "php": ">=7.1", "ext-pdo": "*", "ext-json": "*", + "ext-redis": "*", "swoft/annotation": "^2.0", "swoft/bean": "^2.0", "swoft/event": "^2.0", From ac2d36236c7698b3f0f6b8e41f90a85ebc7bf48f Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 12 Apr 2019 17:36:21 +0800 Subject: [PATCH 314/643] Remove files --- CONTRIBUTING.md | 46 --- Dockerfile | 83 ------ LICENSE | 201 ------------- README.md | 214 -------------- README_CN.md | 215 -------------- app/Boot/MyProcess.php | 45 --- app/Breaker/UserBreaker.php | 50 ---- app/Commands/TestCommand.php | 115 -------- app/Controllers/Admin/DemoController.php | 198 ------------- app/Controllers/Api/RestController.php | 121 -------- app/Controllers/DemoController.php | 181 ------------ app/Controllers/ExceptionController.php | 59 ---- app/Controllers/HttpClientController.php | 34 --- app/Controllers/IndexController.php | 222 -------------- app/Controllers/MiddlewareController.php | 68 ----- app/Controllers/OrmController.php | 290 ------------------- app/Controllers/Psr7Controller.php | 154 ---------- app/Controllers/RedisController.php | 176 ----------- app/Controllers/RestController.php | 121 -------- app/Controllers/RouteController.php | 157 ---------- app/Controllers/RpcController.php | 160 ---------- app/Controllers/SessionController.php | 74 ----- app/Controllers/TaskController.php | 120 -------- app/Controllers/ValidatorController.php | 179 ------------ app/Exception/SwoftExceptionHandler.php | 146 ---------- app/Fallback/DemoServiceFallback.php | 41 --- app/Helper/Functions.php | 12 - app/Lib/DemoInterface.php | 47 --- app/Lib/MdDemoInterface.php | 21 -- app/Listener/TaskFinish.php | 32 -- app/Middlewares/ActionTestMiddleware.php | 45 --- app/Middlewares/ControllerSubMiddleware.php | 42 --- app/Middlewares/ControllerTestMiddleware.php | 36 --- app/Middlewares/GroupTestMiddleware.php | 45 --- app/Middlewares/ServiceMiddleware.php | 44 --- app/Middlewares/ServiceSubMiddleware.php | 42 --- app/Middlewares/SubMiddleware.php | 46 --- app/Middlewares/SubMiddlewares.php | 49 ---- app/Models/Dao/UserDao.php | 33 --- app/Models/Dao/UserExtDao.php | 34 --- app/Models/Data/UserData.php | 39 --- app/Models/Data/UserExtData.php | 44 --- app/Models/Entity/Count.php | 105 ------- app/Models/Entity/User.php | 178 ------------ app/Models/Logic/IndexLogic.php | 50 ---- app/Models/Logic/UserLogic.php | 63 ---- app/Pool/Config/DemoRedisPoolConfig.php | 35 --- app/Pool/Config/UserPoolConfig.php | 118 -------- app/Pool/DemoRedisPool.php | 30 -- app/Pool/UserServicePool.php | 31 -- app/Process/MyProcess.php | 40 --- app/Services/DemoService.php | 59 ---- app/Services/DemoServiceV2.php | 58 ---- app/Services/MiddlewareService.php | 46 --- app/Swoft.php | 14 - app/Tasks/SyncTask.php | 189 ------------ app/WebSocket/EchoController.php | 54 ---- bin/bootstrap.php | 19 -- bin/swoft | 5 - changelog.md | 108 ------- composer.json | 68 ----- config/beans/base.php | 38 --- config/beans/console.php | 12 - config/beans/log.php | 40 --- config/beans/service.php | 11 - config/define.php | 32 -- config/properties/app.php | 30 -- config/properties/breaker.php | 16 - config/properties/cache.php | 31 -- config/properties/db.php | 38 --- config/properties/provider.php | 39 --- config/properties/service.php | 27 -- config/server.php | 68 ----- dev.composer.json | 72 ----- docker-compose.yml | 14 - phar.build.inc | 41 --- phpunit.xml | 21 -- public/.gitkeep | 0 resources/README.md | 4 - resources/languages/en/default.php | 12 - resources/languages/en/msg.php | 12 - resources/languages/zh/default.php | 12 - resources/languages/zh/msg.php | 12 - resources/views/demo/content.php | 32 -- resources/views/demo/view.php | 53 ---- resources/views/exception/index.php | 96 ------ resources/views/index/index.php | 96 ------ resources/views/layouts/default.php | 25 -- resources/views/layouts/default/footer.php | 20 -- resources/views/layouts/default/header.php | 29 -- runtime/logs/.gitkeep | 0 runtime/uploadfiles/.gitkeep | 0 test/Cases/AbstractTestCase.php | 187 ------------ test/Cases/DemoControllerTest.php | 53 ---- test/Cases/IndexControllerTest.php | 131 --------- test/Cases/MiddlewareTest.php | 55 ---- test/Cases/RedisControllerTest.php | 195 ------------- test/Cases/RestTest.php | 109 ------- test/Cases/RouteTest.php | 118 -------- test/Cases/ValidatorControllerTest.php | 201 ------------- test/Dockerfile | 8 - test/README.md | 9 - test/bootstrap.php | 26 -- test/docker-compose.yml | 9 - 104 files changed, 7385 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 Dockerfile delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 README_CN.md delete mode 100644 app/Boot/MyProcess.php delete mode 100644 app/Breaker/UserBreaker.php delete mode 100644 app/Commands/TestCommand.php delete mode 100644 app/Controllers/Admin/DemoController.php delete mode 100644 app/Controllers/Api/RestController.php delete mode 100644 app/Controllers/DemoController.php delete mode 100644 app/Controllers/ExceptionController.php delete mode 100644 app/Controllers/HttpClientController.php delete mode 100644 app/Controllers/IndexController.php delete mode 100644 app/Controllers/MiddlewareController.php delete mode 100644 app/Controllers/OrmController.php delete mode 100644 app/Controllers/Psr7Controller.php delete mode 100644 app/Controllers/RedisController.php delete mode 100644 app/Controllers/RestController.php delete mode 100644 app/Controllers/RouteController.php delete mode 100644 app/Controllers/RpcController.php delete mode 100644 app/Controllers/SessionController.php delete mode 100644 app/Controllers/TaskController.php delete mode 100644 app/Controllers/ValidatorController.php delete mode 100644 app/Exception/SwoftExceptionHandler.php delete mode 100644 app/Fallback/DemoServiceFallback.php delete mode 100644 app/Helper/Functions.php delete mode 100644 app/Lib/DemoInterface.php delete mode 100644 app/Lib/MdDemoInterface.php delete mode 100644 app/Listener/TaskFinish.php delete mode 100644 app/Middlewares/ActionTestMiddleware.php delete mode 100644 app/Middlewares/ControllerSubMiddleware.php delete mode 100644 app/Middlewares/ControllerTestMiddleware.php delete mode 100644 app/Middlewares/GroupTestMiddleware.php delete mode 100644 app/Middlewares/ServiceMiddleware.php delete mode 100644 app/Middlewares/ServiceSubMiddleware.php delete mode 100644 app/Middlewares/SubMiddleware.php delete mode 100644 app/Middlewares/SubMiddlewares.php delete mode 100644 app/Models/Dao/UserDao.php delete mode 100644 app/Models/Dao/UserExtDao.php delete mode 100644 app/Models/Data/UserData.php delete mode 100644 app/Models/Data/UserExtData.php delete mode 100644 app/Models/Entity/Count.php delete mode 100644 app/Models/Entity/User.php delete mode 100644 app/Models/Logic/IndexLogic.php delete mode 100644 app/Models/Logic/UserLogic.php delete mode 100644 app/Pool/Config/DemoRedisPoolConfig.php delete mode 100644 app/Pool/Config/UserPoolConfig.php delete mode 100644 app/Pool/DemoRedisPool.php delete mode 100644 app/Pool/UserServicePool.php delete mode 100644 app/Process/MyProcess.php delete mode 100644 app/Services/DemoService.php delete mode 100644 app/Services/DemoServiceV2.php delete mode 100644 app/Services/MiddlewareService.php delete mode 100644 app/Swoft.php delete mode 100644 app/Tasks/SyncTask.php delete mode 100644 app/WebSocket/EchoController.php delete mode 100644 bin/bootstrap.php delete mode 100644 bin/swoft delete mode 100644 changelog.md delete mode 100644 composer.json delete mode 100644 config/beans/base.php delete mode 100644 config/beans/console.php delete mode 100644 config/beans/log.php delete mode 100644 config/beans/service.php delete mode 100644 config/define.php delete mode 100644 config/properties/app.php delete mode 100644 config/properties/breaker.php delete mode 100644 config/properties/cache.php delete mode 100644 config/properties/db.php delete mode 100644 config/properties/provider.php delete mode 100644 config/properties/service.php delete mode 100644 config/server.php delete mode 100644 dev.composer.json delete mode 100644 docker-compose.yml delete mode 100644 phar.build.inc delete mode 100644 phpunit.xml delete mode 100644 public/.gitkeep delete mode 100644 resources/README.md delete mode 100644 resources/languages/en/default.php delete mode 100644 resources/languages/en/msg.php delete mode 100644 resources/languages/zh/default.php delete mode 100644 resources/languages/zh/msg.php delete mode 100644 resources/views/demo/content.php delete mode 100644 resources/views/demo/view.php delete mode 100644 resources/views/exception/index.php delete mode 100644 resources/views/index/index.php delete mode 100644 resources/views/layouts/default.php delete mode 100644 resources/views/layouts/default/footer.php delete mode 100644 resources/views/layouts/default/header.php delete mode 100644 runtime/logs/.gitkeep delete mode 100644 runtime/uploadfiles/.gitkeep delete mode 100644 test/Cases/AbstractTestCase.php delete mode 100644 test/Cases/DemoControllerTest.php delete mode 100644 test/Cases/IndexControllerTest.php delete mode 100644 test/Cases/MiddlewareTest.php delete mode 100644 test/Cases/RedisControllerTest.php delete mode 100644 test/Cases/RestTest.php delete mode 100644 test/Cases/RouteTest.php delete mode 100644 test/Cases/ValidatorControllerTest.php delete mode 100644 test/Dockerfile delete mode 100644 test/README.md delete mode 100644 test/bootstrap.php delete mode 100644 test/docker-compose.yml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 1c77a503..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,46 +0,0 @@ -# Swoft Contributing Guide - -Hi! I am really excited that you are interested in contributing to Swoft. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines. - -- [Code of Conduct](./.github/CODE_OF_CONDUCT.md) -- [Issue Reporting Guidelines](#issue-reporting-guidelines) -- [Pull Request Guidelines](#pull-request-guidelines) -- [Development Guidelines](#development-guidelines) - -## Issue Reporting Guidelines - -- Should always create an new issues by Github issue template, to avoid missing information. - -## Pull Request Guidelines - -The master branch is the latest stable release, the feature branch commonly is the next feature upgrade version. If it's a feature develoment, should be done in feature branch, if it's a bug-fix develoment, then you could done in master branch, or feature branch, the different between master branch an feature branch is that the feature branch will merge to master branch until next version upgraded, and master branch could release a bug-fix version anytime. - -Note that Swoft using [swoft-component](https://github.com/swoft-cloud/swoft-component) repository to centralized manage all Swoft components, if you want to submit an PR for component of swoft, then you should submit your PR to [swoft-component](https://github.com/swoft-cloud/swoft-component) repository. - -Checkout a topic branch from the relevant branch, e.g. feature, and merge back against that branch. - -It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging. - -Make sure unit test passes, commonly you could use `composer test` to run all unit testes. (see development setup) - -If adding new feature: - -Add accompanying test case. -Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it greenlighted before working on it. -If fixing a bug: - -If you are resolving a special issue, add (fix #xxxx[,#xxx]) (#xxxx is the issue id) in your PR title for a better release log, e.g. update entities encoding/decoding (fix #3899). -Provide detailed description of the bug in the PR. Live demo preferred. -Add appropriate test coverage if applicable. - -## Development Guidelines - -Because Swoft using [swoft-component](https://github.com/swoft-cloud/swoft-component) repository to centralized manage all Swoft components, then you should add `swoft/component` requires to `composer.json` if you are developing in swoft forked repository, after this, components of swoft-component will replace all original components requires, see [Composer replace schema](https://getcomposer.org/doc/04-schema.md#replace) for more details. - -composer requires e.g. - -```json -"require": { - "swoft/component": "dev-master as 1.0" -}, -``` diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index df93f8e5..00000000 --- a/Dockerfile +++ /dev/null @@ -1,83 +0,0 @@ -FROM php:7.1 - -MAINTAINER huangzhhui - -# Version -ENV PHPREDIS_VERSION 4.0.0 -ENV HIREDIS_VERSION 0.13.3 -ENV SWOOLE_VERSION 4.0.3 - -# Timezone -RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ - && echo 'Asia/Shanghai' > /etc/timezone - -# Libs -RUN apt-get update \ - && apt-get install -y \ - curl \ - wget \ - git \ - zip \ - libz-dev \ - libssl-dev \ - libnghttp2-dev \ - libpcre3-dev \ - && apt-get clean \ - && apt-get autoremove - -# Composer -RUN curl -sS https://getcomposer.org/installer | php \ - && mv composer.phar /usr/local/bin/composer \ - && composer self-update --clean-backups - -# PDO extension -RUN docker-php-ext-install pdo_mysql - -# Bcmath extension -RUN docker-php-ext-install bcmath - -# Redis extension -RUN wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \ - && pecl install /tmp/redis.tar.tgz \ - && rm -rf /tmp/redis.tar.tgz \ - && docker-php-ext-enable redis - -# Hiredis -RUN wget https://github.com/redis/hiredis/archive/v${HIREDIS_VERSION}.tar.gz -O hiredis.tar.gz \ - && mkdir -p hiredis \ - && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 \ - && rm hiredis.tar.gz \ - && ( \ - cd hiredis \ - && make -j$(nproc) \ - && make install \ - && ldconfig \ - ) \ - && rm -r hiredis - -# Swoole extension -RUN wget https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz -O swoole.tar.gz \ - && mkdir -p swoole \ - && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ - && rm swoole.tar.gz \ - && ( \ - cd swoole \ - && phpize \ - && ./configure --enable-async-redis --enable-mysqlnd --enable-openssl --enable-http2 \ - && make -j$(nproc) \ - && make install \ - ) \ - && rm -r swoole \ - && docker-php-ext-enable swoole - -ADD . /var/www/swoft - -WORKDIR /var/www/swoft - -RUN composer install --no-dev \ - && composer dump-autoload -o \ - && composer clearcache - -EXPOSE 80 - -ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "start"] diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8dada3ed..00000000 --- a/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md deleted file mode 100644 index cb982fa9..00000000 --- a/README.md +++ /dev/null @@ -1,214 +0,0 @@ -

    -     - swoft - -

    - -[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) -[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) -[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) -[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) -[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) - -**[中文说明](README_CN.md)** - -## Introduction - -The first high-performance PHP coroutine full-stack componentization framework based on Swoole native coroutine, built-in coroutine web server and commonly-used coroutine client, resident memory, which has no dependency on PHP-FPM, asynchronous non-blocking IO implementation, similar to synchronous client style of writing to achieve the use of asynchronous clients, without complex asynchronous callback, no tedious yield, similar Go language coroutines, flexible annotations framework, a powerful global dependency injection container base on annotations, and great service governance , flexible and powerful AOP, PSR specification implementation, etc., could be used to build high-performance Web systems, APIs, middleware, basic services, microservice and so on. - -- Base on Swoole extension -- Built-in HTTP, TCP, WebSocket Coroutine Server -- Powerful AOP (Aspect Oriented Programming) -- Flexible and comprehensive annotations framework -- Global dependency injection container -- PSR-7 based HTTP message implementation -- PSR-14 based event manager -- PSR-15 based middleware -- PSR-16 based cache design -- Scalable high performance RPC -- Holistic service governance, fallback, load balance, service registration and discovery -- Database ORM -- Universal connection pools -- Mysql, Redis, RPC, HTTP Coroutine Clients -- Coroutine driver client and blocking driver client seamlessly switch automatically -- Coroutine and asynchronous task delivery -- Custom user processes -- RESTful supported -- Internationalization (i18n) supported -- High performance router -- Fast and flexible parameter validator -- Alias mechanism -- Powerful log component -- Cross-platform application auto-reload mechanism - - -## Document - -[**Chinese Document**](https://doc.swoft.org) -[**English Document**](https://doc.swoft.org) Not yet, please help us to complete it. - -QQ Group1: 548173319 -QQ Group2: 778656850 - -## Environmental Requirements - -1. PHP 7.0 + -2. [Swoole 2.1.3](https://github.com/swoole/swoole-src/releases) + ( >= 4.1 is better), *coroutine* and *async redis client* options are required -3. [Hiredis](https://github.com/redis/hiredis/releases) -4. [Composer](https://getcomposer.org/) - -## Install - -### Manual Installation - -* Clone project -* Install requires `composer install` - -### Install by Composer - -* `composer create-project swoft/swoft swoft` - -### Install by Docker - -* `docker run -p 80:80 swoft/swoft` - -### Install by Docker-Compose - -* `cd swoft` -* `docker-compose up` - -## Configuration - -If automatically copied `.env` file operation fails when `composer install` was executed, the `.env.example` that in root directory can be manually copied and named `.env`. Note that `composer update` will not trigger related copy operations. - -``` -# Server -PFILE=/tmp/swoft.pid -PNAME=php-swoft -TCPABLE=true -CRONABLE=false -AUTO_RELOAD=true - -# HTTP -HTTP_HOST=0.0.0.0 -HTTP_PORT=80 - -# WebSocket -WS_ENABLE_HTTP=true - -# TCP -TCP_HOST=0.0.0.0 -TCP_PORT=8099 -TCP_PACKAGE_MAX_LENGTH=2048 -TCP_OPEN_EOF_CHECK=false - -# Crontab -CRONTAB_TASK_COUNT=1024 -CRONTAB_TASK_QUEUE=2048 - -# Settings -WORKER_NUM=1 -MAX_REQUEST=10000 -DAEMONIZE=0 -DISPATCH_MODE=2 -LOG_FILE=@runtime/swoole.log -TASK_WORKER_NUM=1 -``` - -## Management - -### Help command - -```text -[root@swoft]# php bin/swoft -h - ____ __ _ -/ ___|_ _____ / _| |_ -\___ \ \ /\ / / _ \| |_| __| - ___) \ V V / (_) | _| |_ -|____/ \_/\_/ \___/|_| \__| - -Usage: - php bin/swoft {command} [arguments ...] [options ...] - -Commands: - entity The group command list of database entity - gen Generate some common application template classes - rpc The group command list of rpc server - server The group command list of http-server - ws There some commands for manage the webSocket server - -Options: - -v, --version show version - -h, --help show help -``` - -### Start HTTP Server - -```bash -// Start HTTP Server -php bin/swoft start - -// Start Daemonize HTTP Server -php bin/swoft start -d - -// Restart HTTP server -php bin/swoft restart - -// Reload HTTP server -php bin/swoft reload - -// Stop HTTP server -php bin/swoft stop -``` - -### Start WebSocket Server - -Start WebSocket Server, optional whether to support HTTP processing. - -```bash -// Star WebSocket Server -php bin/swoft ws:start - -// Start Daemonize WebSocket Server -php bin/swoft ws:start -d - -// Restart WebSocket server -php bin/swoft ws:restart - -// Reload WebSocket server -php bin/swoft ws:reload - -// Stop WebSocket server -php bin/swoft ws:stop -``` - -### Start RPC Server - -Start an independent RPC Server. - -```bash -// Start RPC Server -php bin/swoft rpc:start - -// Start Daemonize RPC Server -php bin/swoft rpc:start -d - -// Restart RPC Server -php bin/swoft rpc:restart - -// Reload RPC Server -php bin/swoft rpc:reload - -// Stop RPC Server -php bin/swoft rpc:stop -``` - -## Changelog - -[Changelog](changelog.md) - -## License - -Swoft is an open-source software licensed under the [LICENSE](LICENSE) diff --git a/README_CN.md b/README_CN.md deleted file mode 100644 index 01724604..00000000 --- a/README_CN.md +++ /dev/null @@ -1,215 +0,0 @@ -

    -     - swoft - -

    - -[![Latest Version](https://img.shields.io/badge/beta-v1.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) -[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Php Version](https://img.shields.io/badge/php-%3E=7.0-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=2.1.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) -[![Hiredis Version](https://img.shields.io/badge/hiredis-%3E=0.1-brightgreen.svg?maxAge=2592000)](https://github.com/redis/hiredis) -[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) -[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) - -**[English](README.md)** - -## 简介 - -首个基于 Swoole 原生协程的新时代 PHP 高性能协程全栈组件化框架,内置协程网络服务器及常用的协程客户端,常驻内存,不依赖传统的 PHP-FPM,全异步非阻塞 IO 实现,以类似于同步客户端的写法实现异步客户端的使用,没有复杂的异步回调,没有繁琐的 yield,有类似 Go 语言的协程、灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等,可以用于构建高性能的Web系统、API、中间件、基础服务等等。 - -- 基于 Swoole 扩展 -- 内置协程 HTTP, TCP, WebSocket 网络服务器 -- 强大的 AOP (面向切面编程) -- 灵活完善的注解功能 -- 全局的依赖注入容器 -- 基于 PSR-7 的 HTTP 消息实现 -- 基于 PSR-14 的事件管理器 -- 基于 PSR-15 的中间件 -- 基于 PSR-16 的缓存设计 -- 可扩展的高性能 RPC -- 完善的服务治理,熔断,降级,负载,注册与发现 -- 数据库 ORM -- 通用连接池 -- 协程 Mysql, Redis, RPC, HTTP 客户端 -- 协程和同步阻塞客户端无缝自动切换 -- 协程、异步任务投递 -- 自定义用户进程 -- RESTful 支持 -- 国际化(i18n)支持 -- 高性能路由 -- 快速灵活的参数验证器 -- 别名机制 -- 强大的日志系统 -- 跨平台热更新自动 Reload - - -## 文档 - -[**中文文档**](https://doc.swoft.org) - -QQ 交流群1: 548173319(已满) -QQ 交流群2: 778656850 - -## 环境要求 - -1. PHP 7.0 + -2. [Swoole 2.1.3](https://github.com/swoole/swoole-src/releases) +, 需开启协程和异步Redis -3. [Hiredis](https://github.com/redis/hiredis/releases) -4. [Composer](https://getcomposer.org/) - -## 安装 - -### 手动安装 - -* Clone 项目 -* 安装依赖 `composer install` - -### Composer 安装 - -* `composer create-project swoft/swoft swoft` - -### Docker 安装 - -* `docker run -p 80:80 swoft/swoft` - -### Docker-Compose 安装 - -* `cd swoft` -* `docker-compose up` - -## 配置 - -若在执行 `composer install` 的时候由程序自动复制环境变量配置文件失败,则可手动复制项目根目录的 `.env.example` 并命名为 `.env`,注意在执行 `composer update` 时并不会触发相关的复制操作 - -``` -# Server -PFILE=/tmp/swoft.pid -PNAME=php-swoft -TCPABLE=true -CRONABLE=false -AUTO_RELOAD=true - -# HTTP -HTTP_HOST=0.0.0.0 -HTTP_PORT=80 - -# WebSocket -WS_ENABLE_HTTP=true - -# TCP -TCP_HOST=0.0.0.0 -TCP_PORT=8099 -TCP_PACKAGE_MAX_LENGTH=2048 -TCP_OPEN_EOF_CHECK=false - -# Crontab -CRONTAB_TASK_COUNT=1024 -CRONTAB_TASK_QUEUE=2048 - -# Settings -WORKER_NUM=1 -MAX_REQUEST=10000 -DAEMONIZE=0 -DISPATCH_MODE=2 -LOG_FILE=@runtime/swoole.log -TASK_WORKER_NUM=1 -``` - -## 管理 - -### 帮助命令 - -```text -[root@swoft]# php bin/swoft -h - ____ __ _ -/ ___|_ _____ / _| |_ -\___ \ \ /\ / / _ \| |_| __| - ___) \ V V / (_) | _| |_ -|____/ \_/\_/ \___/|_| \__| - -Usage: - php bin/swoft {command} [arguments ...] [options ...] - -Commands: - entity The group command list of database entity - gen Generate some common application template classes - rpc The group command list of rpc server - server The group command list of http-server - ws There some commands for manage the webSocket server - -Options: - -v, --version show version - -h, --help show help -``` - -### HTTP Server启动 - -> 是否同时启动RPC服务器取决于.env文件配置 - -```bash -// 启动服务,根据 .env 配置决定是否是守护进程 -php bin/swoft start - -// 守护进程启动,覆盖 .env 守护进程(DAEMONIZE)的配置 -php bin/swoft start -d - -// 重启 -php bin/swoft restart - -// 重新加载 -php bin/swoft reload - -// 关闭服务 -php bin/swoft stop -``` - -### WebSocket Server启动 - -启动WebSocket服务器,可选是否同时支持http处理 - -```bash -// 启动服务,根据 .env 配置决定是否是守护进程 -php bin/swoft ws:start - -// 守护进程启动,覆盖 .env 守护进程(DAEMONIZE)的配置 -php bin/swoft ws:start -d - -// 重启 -php bin/swoft ws:restart - -// 重新加载 -php bin/swoft ws:reload - -// 关闭服务 -php bin/swoft ws:stop -``` - -### RPC Server启动 - -> 启动独立的RPC服务器 - -```bash -// 启动服务,根据 .env 配置决定是否是守护进程 -php bin/swoft rpc:start - -// 守护进程启动,覆盖 .env 守护进程(DAEMONIZE)的配置 -php bin/swoft rpc:start -d - -// 重启 -php bin/swoft rpc:restart - -// 重新加载 -php bin/swoft rpc:reload - -// 关闭服务 -php bin/swoft rpc:stop -``` - -## 更新日志 - -[更新日志](changelog.md) - -## 协议 - -Swoft 的开源协议为 Apache-2.0,详情参见[LICENSE](LICENSE) diff --git a/app/Boot/MyProcess.php b/app/Boot/MyProcess.php deleted file mode 100644 index d87fba7d..00000000 --- a/app/Boot/MyProcess.php +++ /dev/null @@ -1,45 +0,0 @@ -getPname(); - $processName = "$pname myProcess process"; - $process->name($processName); - - echo "Custom boot process \n"; - - $result = Task::deliverByProcess('sync', 'deliverCo', ['p', 'p2']); - var_dump($result); - - ProcessBuilder::create('customProcess')->start(); - } - - public function check(): bool - { - return true; - } -} \ No newline at end of file diff --git a/app/Breaker/UserBreaker.php b/app/Breaker/UserBreaker.php deleted file mode 100644 index 0c254dac..00000000 --- a/app/Breaker/UserBreaker.php +++ /dev/null @@ -1,50 +0,0 @@ -hasOpt('o'); - $opt = input()->getOpt('o'); - $name = input()->getArg('arg', 'swoft'); - - App::trace('this is command log'); - Log::info('this is comamnd info log'); - /* @var UserLogic $logic */ - $logic = App::getBean(UserLogic::class); - $data = $logic->getUserInfo(['uid1']); - var_dump($hasOpt, $opt, $name, $data); - } - - /** - * this task command - * - * @Usage - * test:{command} [arguments] [options] - * - * @Options - * -o,--o this is command option - * - * @Arguments - * arg this is argument - * - * @Example - * php swoft test:task - * - * @Mapping() - */ - public function task() - { - $result = Task::deliver('sync', 'console', ['console']); - var_dump($result); - } -} \ No newline at end of file diff --git a/app/Controllers/Admin/DemoController.php b/app/Controllers/Admin/DemoController.php deleted file mode 100644 index f46c8d09..00000000 --- a/app/Controllers/Admin/DemoController.php +++ /dev/null @@ -1,198 +0,0 @@ -query(); - // 获取name参数默认值defaultName - $getName = $request->query('name', 'defaultName'); - // 获取所有POST参数 - $post = $request->post(); - // 获取name参数默认值defaultName - $postName = $request->post('name', 'defaultName'); - // 获取所有参,包括GET或POST - $inputs = $request->input(); - // 获取name参数默认值defaultName - $inputName = $request->input('name', 'defaultName'); - - return compact('get', 'getName', 'post', 'postName', 'inputs', 'inputName'); - } - - /** - * 定义一个route,支持get,以"/"开头的定义,直接是根路径,处理uri=/index2. - * - * @RequestMapping(route="/index2", method=RequestMethod::GET) - */ - public function index2() - { - Coroutine::create(function () { - App::trace('this is child trace' . Coroutine::id()); - Coroutine::create(function () { - App::trace('this is child child trace' . Coroutine::id()); - }); - }); - - return 'success'; - } - - /** - * 没有使用注解,自动解析注入,默认支持get和post. - */ - public function task() - { - $result = Task::deliver('test', 'corTask', ['params1', 'params2'], Task::TYPE_CO); - $mysql = Task::deliver('test', 'testMysql', [], Task::TYPE_CO); - $http = Task::deliver('test', 'testHttp', [], Task::TYPE_CO, 20); - $rpc = Task::deliver('test', 'testRpc', [], Task::TYPE_CO, 5); - $result1 = Task::deliver('test', 'asyncTask', [], Task::TYPE_ASYNC); - - return [$rpc, $http, $mysql, $result, $result1]; - } - - public function index6() - { - throw new Exception('AAAA'); - // $a = $b; - $A = new AAA(); - - return ['data6']; - } - - /** - * 子协程测试. - */ - public function cor() - { - // 创建子协程 - Coroutine::create(function () { - App::error('child cor error msg'); - App::trace('child cor error msg'); - }); - - // 当前协程id - $cid = Coroutine::id(); - - // 当前运行上下文ID, 协程环境中,顶层协程ID; 任务中,当前任务taskid; 自定义进程中,当前进程ID(pid) - $tid = Coroutine::tid(); - - return [$cid, $tid]; - } - - /** - * 国际化测试. - */ - public function i18n() - { - $data[] = translate('title', [], 'zh'); - $data[] = translate('title', [], 'en'); - $data[] = translate('msg.body', ['stelin', 999], 'en'); - $data[] = translate('msg.body', ['stelin', 666], 'en'); - - return $data; - } - - /** - * 视图渲染demo - 没有使用布局文件. - * - * @RequestMapping() - * @View(template="demo/view") - */ - public function view() - { - $data = [ - 'name' => 'Swoft', - 'repo' => 'https://github.com/swoft-cloud/swoft', - 'doc' => 'https://doc.swoft.org/', - 'doc1' => 'https://swoft-cloud.github.io/swoft-doc/', - 'method' => __METHOD__, - ]; - - return $data; - } - - /** - * 视图渲染demo - 使用布局文件. - * - * @RequestMapping() - * @View(template="demo/content", layout="layouts/default.php") - */ - public function layout() - { - $layout = 'layouts/default.php'; - $data = [ - 'name' => 'Swoft', - 'repo' => 'https://github.com/swoft-cloud/swoft', - 'doc' => 'https://doc.swoft.org/', - 'doc1' => 'https://swoft-cloud.github.io/swoft-doc/', - 'method' => __METHOD__, - 'layoutFile' => $layout, - ]; - - return $data; - } -} diff --git a/app/Controllers/Api/RestController.php b/app/Controllers/Api/RestController.php deleted file mode 100644 index 34cdbc85..00000000 --- a/app/Controllers/Api/RestController.php +++ /dev/null @@ -1,121 +0,0 @@ -input('name'); - - $bodyParams = $request->getBodyParams(); - $bodyParams = empty($bodyParams) ? ['create', $name] : $bodyParams; - - return $bodyParams; - } - - /** - * 查询一个用户信息 - * 地址:/api/user/6. - * - * @RequestMapping(route="{uid}", method={RequestMethod::GET}) - * - * @param int $uid - * - * @return array - */ - public function getUser(int $uid) - { - return ['getUser', $uid]; - } - - /** - * 查询用户的书籍信息 - * 地址:/api/user/6/book/8. - * - * @RequestMapping(route="{userId}/book/{bookId}", method={RequestMethod::GET}) - * - * @param int $userId - * @param string $bookId - * - * @return array - */ - public function getBookFromUser(int $userId, string $bookId) - { - return ['bookFromUser', $userId, $bookId]; - } - - /** - * 删除一个用户信息 - * 地址:/api/user/6. - * - * @RequestMapping(route="{uid}", method={RequestMethod::DELETE}) - * - * @param int $uid - * - * @return array - */ - public function deleteUser(int $uid) - { - return ['delete', $uid]; - } - - /** - * 更新一个用户信息 - * 地址:/api/user/6. - * - * @RequestMapping(route="{uid}", method={RequestMethod::PUT, RequestMethod::PATCH}) - * - * @param int $uid - * @param Request $request - * - * @return array - */ - public function updateUser(Request $request, int $uid) - { - $body = $request->getBodyParams(); - $body['update'] = 'update'; - $body['uid'] = $uid; - - return $body; - } -} diff --git a/app/Controllers/DemoController.php b/app/Controllers/DemoController.php deleted file mode 100644 index 5c50e468..00000000 --- a/app/Controllers/DemoController.php +++ /dev/null @@ -1,181 +0,0 @@ -query(); - // 获取name参数默认值defaultName - $getName = $request->query('name', 'defaultName'); - // 获取所有POST参数 - $post = $request->post(); - // 获取name参数默认值defaultName - $postName = $request->post('name', 'defaultName'); - // 获取所有参,包括GET或POST - $inputs = $request->input(); - // 获取name参数默认值defaultName - $inputName = $request->input('name', 'defaultName'); - - return compact('get', 'getName', 'post', 'postName', 'inputs', 'inputName'); - } - - /** - * 定义一个route,支持get,以"/"开头的定义,直接是根路径,处理uri=/index2 - * @RequestMapping(route="/index2", method=RequestMethod::GET) - */ - public function index2() - { - Coroutine::create(function () { - App::trace('this is child trace' . Coroutine::id()); - Coroutine::create(function () { - App::trace('this is child child trace' . Coroutine::id()); - }); - }); - - return 'success'; - } - - public function index6() - { - throw new Exception('AAAA'); - // $a = $b; - $A = new AAA(); - - return ['data6']; - } - - /** - * 子协程测试 - */ - public function cor() - { - // 创建子协程 - Coroutine::create(function () { - App::error('child cor error msg'); - App::trace('child cor error msg'); - }); - - // 当前协程id - $cid = Coroutine::id(); - - // 当前运行上下文ID, 协程环境中,顶层协程ID; 任务中,当前任务taskid; 自定义进程中,当前进程ID(pid) - $tid = Coroutine::tid(); - - return [$cid, $tid]; - } - - /** - * 国际化测试 - */ - public function i18n() - { - $data[] = translate('title', [], 'zh'); - $data[] = translate('title', [], 'en'); - $data[] = translate('msg.body', ['stelin', 999], 'en'); - $data[] = translate('msg.body', ['stelin', 666], 'en'); - - return $data; - } - - /** - * 视图渲染demo - 没有使用布局文件 - * @RequestMapping() - * @View(template="demo/view") - */ - public function view() - { - $data = [ - 'name' => 'Swoft', - 'repo' => 'https://github.com/swoft-cloud/swoft', - 'doc' => 'https://doc.swoft.org/', - 'doc1' => 'https://swoft-cloud.github.io/swoft-doc/', - 'method' => __METHOD__, - ]; - - return $data; - } - - /** - * 视图渲染demo - 使用布局文件 - * @RequestMapping() - * @View(template="demo/content", layout="layouts/default.php") - */ - public function layout() - { - $layout = 'layouts/default.php'; - $data = [ - 'name' => 'Swoft', - 'repo' => 'https://github.com/swoft-cloud/swoft', - 'doc' => 'https://doc.swoft.org/', - 'doc1' => 'https://swoft-cloud.github.io/swoft-doc/', - 'method' => __METHOD__, - 'layoutFile' => $layout, - ]; - - return $data; - } - -} diff --git a/app/Controllers/ExceptionController.php b/app/Controllers/ExceptionController.php deleted file mode 100644 index ba12d69f..00000000 --- a/app/Controllers/ExceptionController.php +++ /dev/null @@ -1,59 +0,0 @@ -get('http://www.swoft.org/')->getResult(); - $result2 = $client->get('http://www.swoft.org/')->getResponse()->getBody()->getContents(); - return compact('result', 'result2'); - } -} \ No newline at end of file diff --git a/app/Controllers/IndexController.php b/app/Controllers/IndexController.php deleted file mode 100644 index 91f03d52..00000000 --- a/app/Controllers/IndexController.php +++ /dev/null @@ -1,222 +0,0 @@ - 'Home', - 'link' => 'http://www.swoft.org/', - ], - [ - 'name' => 'Documentation', - 'link' => 'http://doc.swoft.org/', - ], - [ - 'name' => 'Case', - 'link' => 'http://swoft.org/case', - ], - [ - 'name' => 'Issue', - 'link' => 'https://github.com/swoft-cloud/swoft/issues', - ], - [ - 'name' => 'GitHub', - 'link' => 'https://github.com/swoft-cloud/swoft', - ], - ]; - // 返回一个 array 或 Arrayable 对象,Response 将根据 Request Header 的 Accept 来返回数据,目前支持 View, Json, Raw - return compact('name', 'notes', 'links'); - } - - /** - * show view by view function - */ - public function templateView(): Response - { - $name = 'Swoft View'; - $notes = [ - 'New Generation of PHP Framework', - 'High Performance, Coroutine and Full Stack' - ]; - $links = [ - [ - 'name' => 'Home', - 'link' => 'http://www.swoft.org/', - ], - [ - 'name' => 'Documentation', - 'link' => 'http://doc.swoft.org/', - ], - [ - 'name' => 'Case', - 'link' => 'http://swoft.org/case', - ], - [ - 'name' => 'Issue', - 'link' => 'https://github.com/swoft-cloud/swoft/issues', - ], - [ - 'name' => 'GitHub', - 'link' => 'https://github.com/swoft-cloud/swoft', - ], - ]; - $data = compact('name', 'notes', 'links'); - - return view('index/index', $data); - } - - /** - * @RequestMapping() - * @View(template="index/index") - * @return \Swoft\Contract\Arrayable|__anonymous@836 - */ - public function arrayable(): Arrayable - { - return new class implements Arrayable - { - /** - * @return array - */ - public function toArray(): array - { - return [ - 'name' => 'Swoft', - 'notes' => ['New Generation of PHP Framework', 'High Performance, Coroutine and Full Stack'], - 'links' => [ - [ - 'name' => 'Home', - 'link' => 'http://www.swoft.org/', - ], - [ - 'name' => 'Documentation', - 'link' => 'http://doc.swoft.org/', - ], - [ - 'name' => 'Case', - 'link' => 'http://swoft.org/case', - ], - [ - 'name' => 'Issue', - 'link' => 'https://github.com/swoft-cloud/swoft/issues', - ], - [ - 'name' => 'GitHub', - 'link' => 'https://github.com/swoft-cloud/swoft', - ], - ] - ]; - } - - }; - } - - /** - * @RequestMapping() - * @return Response - */ - public function absolutePath(): Response - { - $data = [ - 'name' => 'Swoft', - 'notes' => ['New Generation of PHP Framework', 'High Performance, Coroutine and Full Stack'], - 'links' => [ - [ - 'name' => 'Home', - 'link' => 'http://www.swoft.org/', - ], - [ - 'name' => 'Documentation', - 'link' => 'http://doc.swoft.org/', - ], - [ - 'name' => 'Case', - 'link' => 'http://swoft.org/case', - ], - [ - 'name' => 'Issue', - 'link' => 'https://github.com/swoft-cloud/swoft/issues', - ], - [ - 'name' => 'GitHub', - 'link' => 'https://github.com/swoft-cloud/swoft', - ], - ] - ]; - $template = 'index/index'; - return view($template, $data); - } - - /** - * @RequestMapping() - * @return string - */ - public function raw() - { - $name = 'Swoft'; - return $name; - } - - public function testLog() - { - App::trace('this is app trace'); - Log::trace('this is log trace'); - App::error('this is log error'); - Log::trace('this is log error'); - return ['log']; - } - - /** - * @RequestMapping() - * @throws \Swoft\Http\Server\Exception\BadRequestException - */ - public function exception() - { - throw new BadRequestException('bad request exception'); - } - - /** - * @RequestMapping() - * @param Response $response - * @return Response - */ - public function redirect(Response $response): Response - { - return $response->redirect('/'); - } -} diff --git a/app/Controllers/MiddlewareController.php b/app/Controllers/MiddlewareController.php deleted file mode 100644 index 3be7f67e..00000000 --- a/app/Controllers/MiddlewareController.php +++ /dev/null @@ -1,68 +0,0 @@ -setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - - return [$userId]; - } - - public function retEntity() - { - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - $user = User::findById($userId)->getResult(); - - return $user; - } - - public function retEntitys() - { - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - $users = User::findByIds([$userId])->getResult(); - - return $users; - } - - public function findById() - { - $result = User::findById(41710)->getResult(); - $query = User::findById(41710); - - /* @var User $user */ - $user = $query->getResult(User::class); - - return [$result, $user->getName()]; - } - - public function selectDb(){ - $data = [ - 'name' => 'name', - 'sex' => 1, - 'description' => 'this my desc', - 'age' => mt_rand(1, 100), - ]; - $result = Query::table(User::class)->selectDb('test2')->insert($data)->getResult(); - return $result; - } - - public function selectTable(){ - $data = [ - 'name' => 'name', - 'sex' => 1, - 'description' => 'this my desc', - 'age' => mt_rand(1, 100), - ]; - $result = Query::table('user2')->insert($data)->getResult(); - return $result; - } - - public function transactionCommit() - { - Db::beginTransaction(); - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - Db::commit(); - - - return $userId; - } - - public function transactionRollback() - { - Db::beginTransaction(); - - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - - $count = new Count(); - $count->setUid($userId); - $count->setFollows(mt_rand(1, 100)); - $count->setFans(mt_rand(1, 100)); - - $countId = $count->save()->getResult(); - - Db::rollback(); - - - return [$userId, $countId]; - } - - /** - * This is a wrong operation, only used to test - * - * @return mixed - */ - public function transactionNotCommitOrRollback() - { - Db::beginTransaction(); - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - - // This is a wrong operation, You must to commit or rollback - // ... - - return $userId; - } - - /** - * This is a wrong operation, only used to test - * - * @RequestMapping("tsnonr") - * @return mixed - */ - public function transactionNotCommitOrRollbackAndNotGetResult() - { - Db::beginTransaction(); - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save(); - - // This is a wrong operation, You must to commit or rollback - // ... - - return ['11']; - } - - /** - * This is a wrong operation, only used to test - * - * @RequestMapping("tsng") - * @return mixed - */ - public function transactionNotGetResult() - { - Db::beginTransaction(); - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save(); - Db::commit(); - - return [333]; - } - - /** - * This is a wrong operation, only used to test - * - * @RequestMapping("tsng2") - * @return mixed - */ - public function transactionNotGetResult2() - { - Db::beginTransaction(); - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save(); - Db::rollback(); - - return [33]; - } - - /** - * This is a wrong operation, only used to test - * - * @return mixed - */ - public function notGetResult() - { - $result = User::findById(19362); - $query = User::findById(19362); - - /* @var User $user */ - $user = $query->getResult(User::class); - - return [33]; - } - - /** - * This is a wrong operation, only used to test - * - * @return mixed - */ - public function notGetResult2() - { - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save()->getResult(); - - $result = User::findById(19362); - $query = User::findById(19362); - - /* @var User $user */ - $user = $query->getResult(User::class); - - return [222]; - } - - /** - * This is a wrong operation, only used to test - * - * @return mixed - */ - public function notGetResult3() - { - $user = new User(); - $user->setName('name'); - $user->setSex(1); - $user->setDesc('this my desc'); - $user->setAge(mt_rand(1, 100)); - - $userId = $user->save(); - - $result = User::findById(19362); - $query = User::findById(19362); - - /* @var User $user */ - $user = $query->getResult(User::class); - - return [33]; - } -} \ No newline at end of file diff --git a/app/Controllers/Psr7Controller.php b/app/Controllers/Psr7Controller.php deleted file mode 100644 index bb420fcd..00000000 --- a/app/Controllers/Psr7Controller.php +++ /dev/null @@ -1,154 +0,0 @@ -query('param1'); - $param2 = $request->query('param2', 'defaultValue'); - return compact('param1', 'param2'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function post(Request $request): array - { - $param1 = $request->post('param1'); - $param2 = $request->post('param2'); - return compact('param1', 'param2'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function input(Request $request): array - { - $param1 = $request->input('param1'); - $inputs = $request->input(); - return compact('param1', 'inputs'); - } - - /** - * @RequestMapping() - */ - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function raw(Request $request): array - { - $param1 = $request->raw(); - return compact('param1'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function cookies(Request $request): array - { - $cookie1 = $request->cookie(); - return compact('cookie1'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function header(Request $request): array - { - $header1 = $request->header(); - $host = $request->header('host'); - return compact('header1', 'host'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function json(Request $request): array - { - $json = $request->json(); - $jsonParam = $request->json('jsonParam'); - return compact('json', 'jsonParam'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function files(Request $request): array - { - $files = $request->file(); - foreach ($files as $file) { - if ($file instanceof UploadedFileInterface) { - try { - $file->moveTo('@runtime/uploadfiles/' . $file->getClientFilename()); - $move = true; - } catch (\Throwable $e) { - $move = false; - } - } - } - - return compact('move'); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array|null|\Swoft\Http\Message\Upload\UploadedFile - */ - public function multiFilesInOneKey(Request $request) - { - $files = $request->file('files'); - foreach ($files as $file) { - if ($file instanceof UploadedFileInterface) { - try { - $file->moveTo('@runtime/uploadfiles/' . $file->getClientFilename()); - $move[$file->getClientFilename()] = true; - } catch (\Throwable $e) { - $move[$file->getClientFilename()] = false; - } - } - } - - return compact('move'); - } - -} \ No newline at end of file diff --git a/app/Controllers/RedisController.php b/app/Controllers/RedisController.php deleted file mode 100644 index f92ac9fa..00000000 --- a/app/Controllers/RedisController.php +++ /dev/null @@ -1,176 +0,0 @@ -demoRedis->set('name', 'swoft'); - $name = $this->demoRedis->get('name'); - - $this->demoRedis->incr('count'); - $this->demoRedis->incrBy('count2', 2); - - return [$result, $name, $this->demoRedis->get('count'), $this->demoRedis->get('count2'), '3']; - } - - public function testCache() - { - $result = $this->cache->set('name', 'swoft'); - $name = $this->cache->get('name'); - - $this->redis->incr('count'); - - $this->redis->incrBy('count2', 2); - - return [$result, $name, $this->redis->get('count'), $this->redis->get('count2'), '3']; - } - - public function testRedis() - { - $result = $this->redis->set('nameRedis', 'swoft2'); - $name = $this->redis->get('nameRedis'); - - return [$result, $name]; - } - - public function error() - { - $result = $this->redis->set('nameRedis', 'swoft2'); - $name = $this->redis->get('nameRedis'); - $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); - return [$name]; - } - - public function ab() - { - $result1 = User::query()->where('id', '720')->limit(1)->get()->getResult(); - $result2 = $this->redis->set('test1', 1); - - return [$result1, $result2]; - } - - public function ab2() - { - var_dump($this->redis->incr("count")); - var_dump($this->redis->incr("count")); - var_dump($this->redis->incr("count")); - var_dump($this->redis->incr("count")); - $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); - return ['ab']; - } - - public function testFunc() - { - $result = cache()->set('nameFunc', 'swoft3'); - $name = cache()->get('nameFunc'); - - return [$result, $name]; - } - - public function testFunc2() - { - $result = cache()->set('nameFunc2', 'swoft3'); - $name = cache('nameFunc2'); - $name2 = cache('nameFunc3', 'value3'); - - return [$result, $name, $name2]; - } - - public function testDelete() - { - $result = $this->cache->set('name', 'swoft'); - $del = $this->cache->delete('name'); - - return [$result, $del]; - } - - public function clear() - { - $result = $this->cache->clear(); - - return [$result]; - } - - public function setMultiple() - { - $result = $this->cache->setMultiple(['name6' => 'swoft6', 'name8' => 'swoft8']); - $ary = $this->cache->getMultiple(['name6', 'name8']); - - return [$result, $ary]; - } - - public function deleteMultiple() - { - $result = $this->cache->setMultiple(['name6' => 'swoft6', 'name8' => 'swoft8']); - $ary = $this->cache->deleteMultiple(['name6', 'name8']); - - return [$result, $ary]; - } - - public function has() - { - $result = $this->cache->set('name666', 'swoft666'); - $ret = $this->cache->has('name666'); - - return [$result, $ret]; - } - - public function testDefer() - { - $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); -// $ret2 = $this->redis->deferCall('set', ['name2', 'swoft2']); - - $r1 = $ret1->getResult(); - $r2 = 1; -// $r2 = $ret2->getResult(); - - $ary = 1; - // $ary = $this->redis->getMultiple(['name1', 'name2']); - - return [$r1, $r2, $ary]; - } - - public function deferError() - { - $ret1 = $this->redis->deferCall('set', ['name1', 'swoft1']); - return 'error'; - } -} \ No newline at end of file diff --git a/app/Controllers/RestController.php b/app/Controllers/RestController.php deleted file mode 100644 index 4b65e1a6..00000000 --- a/app/Controllers/RestController.php +++ /dev/null @@ -1,121 +0,0 @@ -input('name'); - - $bodyParams = $request->getBodyParams(); - $bodyParams = empty($bodyParams) ? ['create', $name] : $bodyParams; - - return $bodyParams; - } - - /** - * 查询一个用户信息 - * 地址:/user/6 - * - * @RequestMapping(route="{uid}", method={RequestMethod::GET}) - * - * @param int $uid - * - * @return array - */ - public function getUser(int $uid) - { - return ['getUser', $uid]; - } - - /** - * 查询用户的书籍信息 - * 地址:/user/6/book/8 - * - * @RequestMapping(route="{userId}/book/{bookId}", method={RequestMethod::GET}) - * - * @param int $userId - * @param string $bookId - * - * @return array - */ - public function getBookFromUser(int $userId, string $bookId) - { - return ['bookFromUser', $userId, $bookId]; - } - - /** - * 删除一个用户信息 - * 地址:/user/6 - * - * @RequestMapping(route="{uid}", method={RequestMethod::DELETE}) - * - * @param int $uid - * - * @return array - */ - public function deleteUser(int $uid) - { - return ['delete', $uid]; - } - - /** - * 更新一个用户信息 - * 地址:/user/6 - * - * @RequestMapping(route="{uid}", method={RequestMethod::PUT, RequestMethod::PATCH}) - * - * @param int $uid - * @param Request $request - * @return array - */ - public function updateUser(Request $request, int $uid) - { - $body = $request->getBodyParams(); - $body['update'] = 'update'; - $body['uid'] = $uid; - - return $body; - } -} \ No newline at end of file diff --git a/app/Controllers/RouteController.php b/app/Controllers/RouteController.php deleted file mode 100644 index 6e9a3d05..00000000 --- a/app/Controllers/RouteController.php +++ /dev/null @@ -1,157 +0,0 @@ - $router->getStaticRoutes(), - 'regular' => $router->getRegularRoutes(), - 'vague' => $router->getVagueRoutes(), - 'cached' => $router->getCacheRoutes(), - ]; - } - - /** - * @RequestMapping(route="user/{uid}/book/{bid}/{bool}/{name}") - * - * @param bool $bool - * @param Request $request - * @param int $bid - * @param string $name - * @param int $uid - * @param Response $response - * - * @return array - */ - public function funcArgs(bool $bool, Request $request, int $bid, string $name, int $uid, Response $response) - { - return [$bid, $uid, $bool, $name, get_class($request), get_class($response)]; - } - - /** - * @RequestMapping(route="hasNotArg") - * - * @return string - */ - public function hasNotArgs() - { - return 'hasNotArg'; - } - - /** - * @RequestMapping(route="hasAnyArgs/{bid}") - * @param Request $request - * @param int $bid - * - * @return string - */ - public function hasAnyArgs(Request $request, int $bid) - { - return [get_class($request), $bid]; - } - - /** - * @RequestMapping(route="hasMoreArgs") - * - * @param Request $request - * @param int $bid - * - * @return array - */ - public function hasMoreArgs(Request $request, int $bid) - { - return [get_class($request), $bid]; - } - - /** - * optional parameter - * - * @RequestMapping(route="opntion[/{name}]") - * - * @param string $name - * @return array - */ - public function optionalParameter(string $name) - { - return[$name]; - } - - /** - * optional parameter - * - * @RequestMapping(route="anyName/{name}") - * - * @param string $name - * @return array - */ - public function funcAnyName(string $name) - { - return [$name]; - } - - /** - * @param Request $request - * - * @return array - */ - public function notAnnotation(Request $request) - { - return [get_class($request)]; - } - - /** - * @param Request $request - * - * @return array - */ - public function onlyFunc(Request $request) - { - return [get_class($request)]; - } - - /** - * @param Request $request - * - * @return array - */ - public function behind(Request $request) - { - return [get_class($request)]; - } -} diff --git a/app/Controllers/RpcController.php b/app/Controllers/RpcController.php deleted file mode 100644 index f2f20905..00000000 --- a/app/Controllers/RpcController.php +++ /dev/null @@ -1,160 +0,0 @@ -demoService->getUser('11'); - $result2 = $this->demoService->getUsers(['1','2']); - $result3 = $this->demoService->getUserByCond(1, 2, 'boy', 1.6); - - return [ - $result1, - $result2, - $result3, - ]; - } - - /** - * @return array - */ - public function deferFallback() - { - $result1 = $this->demoService->deferGetUser('11')->getResult(); - $result2 = $this->demoService->deferGetUsers(['1','2'])->getResult(); - $result3 = $this->demoService->deferGetUserByCond(1, 2, 'boy', 1.6)->getResult(); - - return [ - 'defer', - $result1, - $result2, - $result3, - ]; - } - - /** - * @RequestMapping(route="call") - * @return array - */ - public function call() - { - $version = $this->demoService->getUser('11'); - $version2 = $this->demoServiceV2->getUser('11'); - - return [ - 'version' => $version, - 'version2' => $version2, - ]; - } - - /** - * Defer call - */ - public function defer(){ - $defer1 = $this->demoService->deferGetUser('123'); - $defer2 = $this->demoServiceV2->deferGetUsers(['2', '3']); - $defer3 = $this->demoServiceV2->deferGetUserByCond(1, 2, 'boy', 1.6); - - $result1 = $defer1->getResult(); - $result2 = $defer2->getResult(); - $result3 = $defer3->getResult(); - - return [$result1, $result2, $result3]; - } - - public function deferError() - { - $defer1 = $this->demoService->deferGetUser('123'); - return ['error']; - } - - public function beanCall() - { - return [ - $this->logic->rpcCall() - ]; - } - - /** - * @RequestMapping("validate") - */ - public function validate() - { - $result = $this->demoService->getUserByCond(1, 2, 'boy', '4'); - - return ['validator', $result]; - } - - - /** - * @RequestMapping("pm") - */ - public function parentMiddleware() - { - $result = $this->mdDemoService->parentMiddleware(); - - return ['parentMiddleware', $result]; - } - - /** - * @RequestMapping("fm") - */ - public function funcMiddleware() - { - $result = $this->mdDemoService->funcMiddleware(); - - return ['funcMiddleware', $result]; - } -} \ No newline at end of file diff --git a/app/Controllers/SessionController.php b/app/Controllers/SessionController.php deleted file mode 100644 index 15d5b1f0..00000000 --- a/app/Controllers/SessionController.php +++ /dev/null @@ -1,74 +0,0 @@ -all(); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function set(Request $request): array - { - $key = $request->input('key'); - $value = $request->input('value'); - session()->put([$key => $value]); - return session()->all(); - } - - /** - * @RequestMapping() - * @param \Swoft\Http\Message\Server\Request $request - * @return array - */ - public function remove(Request $request): array - { - $key = $request->input('key'); - session()->remove($key); - return session()->all(); - } - - /** - * @RequestMapping() - */ - public function flush() - { - session()->flush(); - return session()->all(); - } - - /** - * @RequestMapping() - */ - public function regenerateId() - { - return session()->migrate(true); - } -} \ No newline at end of file diff --git a/app/Controllers/TaskController.php b/app/Controllers/TaskController.php deleted file mode 100644 index a0a46dfc..00000000 --- a/app/Controllers/TaskController.php +++ /dev/null @@ -1,120 +0,0 @@ -query('name'); - $postName = $request->post('name'); - - return [$getName, $postName, $name]; - } - - /** - * @RequestMapping("stringTpl") - * @Strings(from=ValidatorFrom::GET, name="name", min=3, max=10, template="{name}-{min}-{max} must") - * @return string - */ - public function stringTpl() - { - return 'stringTpl'; - } - - /** - * @RequestMapping("number/{id}") - * - * @Number(from=ValidatorFrom::GET, name="id", min=5, max=10, default=7) - * @Number(from=ValidatorFrom::POST, name="id", min=5, max=10, default=8) - * @Number(from=ValidatorFrom::PATH, name="id", min=5, max=10) - * - * @param Request $request - * @param int $id - * - * @return array - */ - public function number(Request $request, int $id) - { - $get = $request->query('id'); - $post = $request->post('id'); - - return [$get, $post, $id]; - } - - /** - * @RequestMapping("numberTpl") - * @Number(from=ValidatorFrom::GET, name="id", min=5, max=10, template="{name}-{min}-{max} must") - * @return string - */ - public function numberTpl() - { - return 'numberTpl'; - } - - /** - * @RequestMapping("integer/{id}") - * - * @Integer(from=ValidatorFrom::GET, name="id", min=5, max=10, default=7) - * @Integer(from=ValidatorFrom::POST, name="id", min=5, max=10, default=8) - * @Integer(from=ValidatorFrom::PATH, name="id", min=5, max=10) - * - * @param Request $request - * @param int $id - * - * @return array - */ - public function integer(Request $request, int $id) - { - $get = $request->query('id'); - $post = $request->post('id'); - - return [$get, $post, $id]; - } - - /** - * @RequestMapping("integerTpl") - * @Integer(from=ValidatorFrom::GET, name="id", min=5, max=10, template="{name}-{min}-{max} must") - * @return string - */ - public function integerTpl() - { - return 'integerTpl'; - } - - /** - * @RequestMapping("float/{id}") - * - * @Floats(from=ValidatorFrom::GET, name="id", min=5.1, max=5.9, default=5.6) - * @Floats(from=ValidatorFrom::POST, name="id", min=5.1, max=5.9, default=5.6) - * @Floats(from=ValidatorFrom::PATH, name="id", min=5.1, max=5.9) - * - * @param Request $request - * @param float $id - * - * @return array - */ - public function float(Request $request, float $id) - { - $get = $request->query('id'); - $post = $request->post('id'); - - return [$get, $post, $id]; - } - - /** - * @RequestMapping("floatTpl") - * @Floats(from=ValidatorFrom::GET, name="id", min=5.1, max=5.9, template="{name}-{min}-{max} must") - * @return string - */ - public function floatTpl() - { - return 'floatTpl'; - } - - - /** - * @RequestMapping("enum/{name}") - * - * @Enum(from=ValidatorFrom::GET, name="name", values={1,"a",3}, default=1) - * @Enum(from=ValidatorFrom::POST, name="name", values={1,"a",3}, default=1) - * @Enum(from=ValidatorFrom::PATH, name="name", values={1,"a",3}, default=1) - * - * @param string $name - * @param Request $request - * - * @return array - */ - public function estring(Request $request, $name) - { - $getName = $request->query('name'); - $postName = $request->post('name'); - - return [$getName, $postName, $name]; - } - - /** - * @RequestMapping("enumTpl") - * @Enum(from=ValidatorFrom::GET, name="name", values={1,"a",3}, template="{name}-{value} must") - * @return string - */ - public function enumTpl() - { - return 'enumTpl'; - } - -} \ No newline at end of file diff --git a/app/Exception/SwoftExceptionHandler.php b/app/Exception/SwoftExceptionHandler.php deleted file mode 100644 index 242049f2..00000000 --- a/app/Exception/SwoftExceptionHandler.php +++ /dev/null @@ -1,146 +0,0 @@ - - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class SwoftExceptionHandler -{ - /** - * @Handler(Exception::class) - * - * @param Response $response - * @param \Throwable $throwable - * - * @return Response - */ - public function handlerException(Response $response, \Throwable $throwable) - { - $file = $throwable->getFile(); - $line = $throwable->getLine(); - $code = $throwable->getCode(); - $exception = $throwable->getMessage(); - - $data = ['msg' => $exception, 'file' => $file, 'line' => $line, 'code' => $code]; - App::error(json_encode($data)); - return $response->json($data); - } - - /** - * @Handler(RuntimeException::class) - * - * @param Response $response - * @param \Throwable $throwable - * - * @return Response - */ - public function handlerRuntimeException(Response $response, \Throwable $throwable) - { - $file = $throwable->getFile(); - $code = $throwable->getCode(); - $exception = $throwable->getMessage(); - - return $response->json([$exception, 'runtimeException']); - } - - /** - * @Handler(ValidatorException::class) - * - * @param Response $response - * @param \Throwable $throwable - * - * @return Response - */ - public function handlerValidatorException(Response $response, \Throwable $throwable) - { - $exception = $throwable->getMessage(); - - return $response->json(['message' => $exception]); - } - - /** - * @Handler(BadRequestException::class) - * - * @param Response $response - * @param \Throwable $throwable - * - * @return Response - */ - public function handlerBadRequestException(Response $response, \Throwable $throwable) - { - $exception = $throwable->getMessage(); - - return $response->json(['message' => $exception]); - } - - /** - * @Handler(BadMethodCallException::class) - * - * @param Request $request - * @param Response $response - * @param \Throwable $throwable - * - * @return Response - */ - public function handlerViewException(Request $request, Response $response, \Throwable $throwable) - { - $name = $throwable->getMessage(). $request->getUri()->getPath(); - $notes = [ - 'New Generation of PHP Framework', - 'High Performance, Coroutine and Full Stack', - ]; - $links = [ - [ - 'name' => 'Home', - 'link' => 'http://www.swoft.org/', - ], - [ - 'name' => 'Documentation', - 'link' => 'http://doc.swoft.org/', - ], - [ - 'name' => 'Case', - 'link' => 'http://swoft.org/case', - ], - [ - 'name' => 'Issue', - 'link' => 'https://github.com/swoft-cloud/swoft/issues', - ], - [ - 'name' => 'GitHub', - 'link' => 'https://github.com/swoft-cloud/swoft', - ], - ]; - $data = compact('name', 'notes', 'links'); - - return view('exception/index', $data); - } - -} \ No newline at end of file diff --git a/app/Fallback/DemoServiceFallback.php b/app/Fallback/DemoServiceFallback.php deleted file mode 100644 index 33cdae6f..00000000 --- a/app/Fallback/DemoServiceFallback.php +++ /dev/null @@ -1,41 +0,0 @@ - - * [ - * 'uid' => [], - * 'uid2' => [], - * ...... - * ] - *
    -     */
    -    public function getUsers(array $ids);
    -
    -    /**
    -     * @param string $id
    -     *
    -     * @return array
    -     */
    -    public function getUser(string $id);
    -
    -    public function getUserByCond(int $type, int $uid, string $name, float $price, string $desc = 'desc');
    -}
    \ No newline at end of file
    diff --git a/app/Lib/MdDemoInterface.php b/app/Lib/MdDemoInterface.php
    deleted file mode 100644
    index 1a2c2664..00000000
    --- a/app/Lib/MdDemoInterface.php
    +++ /dev/null
    @@ -1,21 +0,0 @@
    -getParams());
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/ActionTestMiddleware.php b/app/Middlewares/ActionTestMiddleware.php
    deleted file mode 100644
    index 5abb74a6..00000000
    --- a/app/Middlewares/ActionTestMiddleware.php
    +++ /dev/null
    @@ -1,45 +0,0 @@
    -
    - * @copyright Copyright 2010-2017 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class ActionTestMiddleware implements MiddlewareInterface
    -{
    -
    -    /**
    -     * Process an incoming server request and return a response, optionally delegating
    -     * response creation to a handler.
    -     *
    -     * @param \Psr\Http\Message\ServerRequestInterface $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     * @return \Psr\Http\Message\ResponseInterface
    -     * @throws \InvalidArgumentException
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        $response = $handler->handle($request);
    -        return $response->withAddedHeader('Middleware-Action-Test', 'success');
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/ControllerSubMiddleware.php b/app/Middlewares/ControllerSubMiddleware.php
    deleted file mode 100644
    index 4c10c72e..00000000
    --- a/app/Middlewares/ControllerSubMiddleware.php
    +++ /dev/null
    @@ -1,42 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class ControllerSubMiddleware implements MiddlewareInterface
    -{
    -    /**
    -     * @param \Psr\Http\Message\ServerRequestInterface $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     * @return \Psr\Http\Message\ResponseInterface
    -     * @throws \InvalidArgumentException
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        $response = $handler->handle($request);
    -        return $response->withAddedHeader('Controller-Sub-Middleware', 'success');
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/ControllerTestMiddleware.php b/app/Middlewares/ControllerTestMiddleware.php
    deleted file mode 100644
    index ee6d2c82..00000000
    --- a/app/Middlewares/ControllerTestMiddleware.php
    +++ /dev/null
    @@ -1,36 +0,0 @@
    -handle($request);
    -        return $response->withAddedHeader('Controller-Test-Middleware', 'success');
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/GroupTestMiddleware.php b/app/Middlewares/GroupTestMiddleware.php
    deleted file mode 100644
    index 83458e92..00000000
    --- a/app/Middlewares/GroupTestMiddleware.php
    +++ /dev/null
    @@ -1,45 +0,0 @@
    -
    - * @copyright Copyright 2010-2017 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class GroupTestMiddleware implements MiddlewareInterface
    -{
    -
    -    /**
    -     * Process an incoming server request and return a response, optionally delegating
    -     * response creation to a handler.
    -     *
    -     * @param \Psr\Http\Message\ServerRequestInterface $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     * @return \Psr\Http\Message\ResponseInterface
    -     * @throws \InvalidArgumentException
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        $response = $handler->handle($request);
    -        return $response->withAddedHeader('Middleware-Group-Test', 'success');
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/ServiceMiddleware.php b/app/Middlewares/ServiceMiddleware.php
    deleted file mode 100644
    index bbc7f5c0..00000000
    --- a/app/Middlewares/ServiceMiddleware.php
    +++ /dev/null
    @@ -1,44 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class ServiceMiddleware implements MiddlewareInterface
    -{
    -    /**
    -     * @param \Psr\Http\Message\ServerRequestInterface     $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     *
    -     * @return \Psr\Http\Message\ResponseInterface
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        var_dump('ServiceMiddleware->before');
    -        $response = $handler->handle($request);
    -        var_dump('ServiceMiddleware->after');
    -        return $response;
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/ServiceSubMiddleware.php b/app/Middlewares/ServiceSubMiddleware.php
    deleted file mode 100644
    index 32b42439..00000000
    --- a/app/Middlewares/ServiceSubMiddleware.php
    +++ /dev/null
    @@ -1,42 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class ServiceSubMiddleware implements MiddlewareInterface
    -{
    -    /**
    -     * @param \Psr\Http\Message\ServerRequestInterface     $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     *
    -     * @return \Psr\Http\Message\ResponseInterface
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        var_dump('ServiceSubMiddleware->before');
    -        return $handler->handle($request);
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/SubMiddleware.php b/app/Middlewares/SubMiddleware.php
    deleted file mode 100644
    index 5b830a93..00000000
    --- a/app/Middlewares/SubMiddleware.php
    +++ /dev/null
    @@ -1,46 +0,0 @@
    -
    - * @copyright Copyright 2010-2017 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class SubMiddleware implements MiddlewareInterface
    -{
    -
    -    /**
    -     * Process an incoming server request and return a response, optionally delegating
    -     * response creation to a handler.
    -     *
    -     * @param \Psr\Http\Message\ServerRequestInterface $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     * @return \Psr\Http\Message\ResponseInterface
    -     * @throws \InvalidArgumentException
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        $response = $handler->handle($request);
    -        $response = $response->withAddedHeader('Sub-Middleware-Test', 'success');
    -        return $response;
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Middlewares/SubMiddlewares.php b/app/Middlewares/SubMiddlewares.php
    deleted file mode 100644
    index 159340c5..00000000
    --- a/app/Middlewares/SubMiddlewares.php
    +++ /dev/null
    @@ -1,49 +0,0 @@
    -
    - * @copyright Copyright 2010-2017 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class SubMiddlewares implements MiddlewareInterface
    -{
    -
    -    /**
    -     * Process an incoming server request and return a response, optionally delegating
    -     * response creation to a handler.
    -     *
    -     * @param \Psr\Http\Message\ServerRequestInterface $request
    -     * @param \Psr\Http\Server\RequestHandlerInterface $handler
    -     * @return \Psr\Http\Message\ResponseInterface
    -     */
    -    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    -    {
    -        if ($handler instanceof RequestHandler) {
    -            $handler->insertMiddlewares([
    -                SubMiddleware::class,
    -            ]);
    -        }
    -        return $handler->handle($request);
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Models/Dao/UserDao.php b/app/Models/Dao/UserDao.php
    deleted file mode 100644
    index e788180d..00000000
    --- a/app/Models/Dao/UserDao.php
    +++ /dev/null
    @@ -1,33 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class UserDao
    -{
    -    public function getUserInfo()
    -    {
    -        return [
    -            'uid' => 666,
    -            'name' => 'stelin'
    -        ];
    -    }
    -}
    diff --git a/app/Models/Dao/UserExtDao.php b/app/Models/Dao/UserExtDao.php
    deleted file mode 100644
    index 89de9aa4..00000000
    --- a/app/Models/Dao/UserExtDao.php
    +++ /dev/null
    @@ -1,34 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class UserExtDao
    -{
    -    public function getExtInfo()
    -    {
    -        return [
    -            'age' => 18,
    -            'desc' => 'hello',
    -            'address' => 'chengdu'
    -        ];
    -    }
    -}
    diff --git a/app/Models/Data/UserData.php b/app/Models/Data/UserData.php
    deleted file mode 100644
    index 89199717..00000000
    --- a/app/Models/Data/UserData.php
    +++ /dev/null
    @@ -1,39 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class UserData
    -{
    -    /**
    -     *
    -     * @Inject()
    -     * @var UserDao
    -     */
    -    private $userDao;
    -
    -    public function getUserInfo()
    -    {
    -        return $this->userDao->getUserInfo();
    -    }
    -}
    diff --git a/app/Models/Data/UserExtData.php b/app/Models/Data/UserExtData.php
    deleted file mode 100644
    index d7100060..00000000
    --- a/app/Models/Data/UserExtData.php
    +++ /dev/null
    @@ -1,44 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class UserExtData
    -{
    -    /**
    -     * @Inject()
    -     * @var UserExtDao
    -     */
    -    private $userExtDao;
    -
    -    /**
    -     * @Inject()
    -     * @var UserData
    -     */
    -    private $userData;
    -
    -    public function getExtInfo()
    -    {
    -        return $this->userExtDao->getExtInfo();
    -    }
    -}
    diff --git a/app/Models/Entity/Count.php b/app/Models/Entity/Count.php
    deleted file mode 100644
    index b7785622..00000000
    --- a/app/Models/Entity/Count.php
    +++ /dev/null
    @@ -1,105 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class Count extends Model
    -{
    -    /**
    -     * 用户ID
    -     *
    -     * @Column(name="uid", type=Types::INT)
    -     * @Id()
    -     * @var null|int
    -     */
    -    private $uid;
    -
    -    /**
    -     * 粉丝数
    -     *
    -     * @Column(name="fans", type=Types::NUMBER)
    -     * @var int
    -     */
    -    private $fans = 0;
    -
    -    /**
    -     * 关注数
    -     *
    -     * @Column("follows", type=Types::NUMBER)
    -     * @var int
    -     */
    -    private $follows = 0;
    -
    -    /**
    -     * @return int|null
    -     */
    -    public function getUid()
    -    {
    -        return $this->uid;
    -    }
    -
    -    /**
    -     * @param int|null $uid
    -     */
    -    public function setUid($uid)
    -    {
    -        $this->uid = $uid;
    -    }
    -
    -    /**
    -     * @return int
    -     */
    -    public function getFans(): int
    -    {
    -        return $this->fans;
    -    }
    -
    -    /**
    -     * @param int $fans
    -     */
    -    public function setFans(int $fans)
    -    {
    -        $this->fans = $fans;
    -    }
    -
    -    /**
    -     * @return mixed
    -     */
    -    public function getFollows()
    -    {
    -        return $this->follows;
    -    }
    -
    -    /**
    -     * @param mixed $follows
    -     */
    -    public function setFollows($follows)
    -    {
    -        $this->follows = $follows;
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Models/Entity/User.php b/app/Models/Entity/User.php
    deleted file mode 100644
    index 3ecf8d18..00000000
    --- a/app/Models/Entity/User.php
    +++ /dev/null
    @@ -1,178 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class User extends Model
    -{
    -    /**
    -     * 主键ID
    -     *
    -     * @Id()
    -     * @Column(name="id", type=Types::INT)
    -     * @var null|int
    -     */
    -    private $id;
    -
    -    /**
    -     * 名称
    -     *
    -     * @Column(name="name", type=Types::STRING, length=20)
    -     * @Required()
    -     * @var null|string
    -     */
    -    private $name;
    -
    -    /**
    -     * 年龄
    -     *
    -     * @Column(name="age", type=Types::INT)
    -     * @var int
    -     */
    -    private $age = 0;
    -
    -    /**
    -     * 性别
    -     *
    -     * @Column(name="sex", type="int")
    -     * @var int
    -     */
    -    private $sex = 0;
    -
    -    /**
    -     * 描述
    -     *
    -     * @Column(name="description", type="string")
    -     * @var string
    -     */
    -    private $desc = '';
    -
    -    /**
    -     * 非数据库字段,未定义映射关系
    -     *
    -     * @var mixed
    -     */
    -    private $otherProperty;
    -
    -    /**
    -     * @return int|null
    -     */
    -    public function getId()
    -    {
    -        return $this->id;
    -    }
    -
    -    /**
    -     * @param int|null $id
    -     */
    -    public function setId($id)
    -    {
    -        $this->id = $id;
    -    }
    -
    -    /**
    -     * @return null|string
    -     */
    -    public function getName()
    -    {
    -        return $this->name;
    -    }
    -
    -    /**
    -     * @param null|string $name
    -     */
    -    public function setName($name)
    -    {
    -        $this->name = $name;
    -    }
    -
    -    /**
    -     * @return int
    -     */
    -    public function getAge(): int
    -    {
    -        return $this->age;
    -    }
    -
    -    /**
    -     * @param int $age
    -     */
    -    public function setAge(int $age)
    -    {
    -        $this->age = $age;
    -    }
    -
    -    /**
    -     * @return int
    -     */
    -    public function getSex(): int
    -    {
    -        return $this->sex;
    -    }
    -
    -    /**
    -     * @param int $sex
    -     */
    -    public function setSex(int $sex)
    -    {
    -        $this->sex = $sex;
    -    }
    -
    -    /**
    -     * @return string
    -     */
    -    public function getDesc(): string
    -    {
    -        return $this->desc;
    -    }
    -
    -    /**
    -     * @param string $desc
    -     */
    -    public function setDesc(string $desc)
    -    {
    -        $this->desc = $desc;
    -    }
    -
    -    /**
    -     * @return mixed
    -     */
    -    public function getOtherProperty()
    -    {
    -        return $this->otherProperty;
    -    }
    -
    -    /**
    -     * @param mixed $otherProperty
    -     */
    -    public function setOtherProperty($otherProperty)
    -    {
    -        $this->otherProperty = $otherProperty;
    -    }
    -}
    diff --git a/app/Models/Logic/IndexLogic.php b/app/Models/Logic/IndexLogic.php
    deleted file mode 100644
    index 9cd849e9..00000000
    --- a/app/Models/Logic/IndexLogic.php
    +++ /dev/null
    @@ -1,50 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 Swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class IndexLogic
    -{
    -    /**
    -     *
    -     * @Inject()
    -     * @var UserData
    -     */
    -    private $userData;
    -
    -    /**
    -     *
    -     * @Inject()
    -     * @var UserExtData
    -     */
    -    private $userExtData;
    -
    -    public function getUser()
    -    {
    -        $base = $this->userData->getUserInfo();
    -        $ext = $this->userExtData->getExtInfo();
    -
    -        return array_merge($base, $ext);
    -    }
    -}
    diff --git a/app/Models/Logic/UserLogic.php b/app/Models/Logic/UserLogic.php
    deleted file mode 100644
    index b1783302..00000000
    --- a/app/Models/Logic/UserLogic.php
    +++ /dev/null
    @@ -1,63 +0,0 @@
    -
    - * @copyright Copyright 2010-2016 swoft software
    - * @license   PHP Version 7.x {@link http://www.php.net/license/3_0.txt}
    - */
    -class UserLogic
    -{
    -    /**
    -     * @Reference("user")
    -     *
    -     * @var \App\Lib\DemoInterface
    -     */
    -    private $demoService;
    -
    -    /**
    -     * @Reference(name="user", version="1.0.1")
    -     *
    -     * @var \App\Lib\DemoInterface
    -     */
    -    private $demoServiceV2;
    -
    -    public function rpcCall()
    -    {
    -        return ['bean', $this->demoService->getUser('12'), $this->demoServiceV2->getUser('16')];
    -    }
    -
    -    public function getUserInfo(array $uids)
    -    {
    -        $user = [
    -            'name' => 'boby',
    -            'desc' => 'this is boby'
    -        ];
    -
    -        $data = [];
    -        foreach ($uids as $uid) {
    -            $user['uid'] = $uid;
    -            $data[] = $user;
    -        }
    -
    -        return $data;
    -    }
    -}
    \ No newline at end of file
    diff --git a/app/Pool/Config/DemoRedisPoolConfig.php b/app/Pool/Config/DemoRedisPoolConfig.php
    deleted file mode 100644
    index 087b427c..00000000
    --- a/app/Pool/Config/DemoRedisPoolConfig.php
    +++ /dev/null
    @@ -1,35 +0,0 @@
    -
    -     * [
    -     *  '127.0.0.1:88',
    -     *  '127.0.0.1:88'
    -     * ]
    -     * 
    - * - * @Value(name="${config.service.user.uri}", env="${USER_POOL_URI}") - * @var array - */ - protected $uri = []; - - /** - * whether to user provider(consul/etcd/zookeeper) - * - * @Value(name="${config.service.user.useProvider}", env="${USER_POOL_USE_PROVIDER}") - * @var bool - */ - protected $useProvider = false; - - /** - * the default balancer is random balancer - * - * @Value(name="${config.service.user.balancer}", env="${USER_POOL_BALANCER}") - * @var string - */ - protected $balancer = ''; - - /** - * the default provider is consul provider - * - * @Value(name="${config.service.user.provider}", env="${USER_POOL_PROVIDER}") - * @var string - */ - protected $provider = ''; -} diff --git a/app/Pool/DemoRedisPool.php b/app/Pool/DemoRedisPool.php deleted file mode 100644 index fc0a9942..00000000 --- a/app/Pool/DemoRedisPool.php +++ /dev/null @@ -1,30 +0,0 @@ -getPname(); - $processName = "$pname myProcess process"; - $process->name($processName); - - echo "Custom child process \n"; - var_dump(Coroutine::id()); - } - - public function check(): bool - { - return true; - } -} \ No newline at end of file diff --git a/app/Services/DemoService.php b/app/Services/DemoService.php deleted file mode 100644 index 3bde2d77..00000000 --- a/app/Services/DemoService.php +++ /dev/null @@ -1,59 +0,0 @@ -deferCall('set', ['name1', 'swoft1'])->getResult(); - $ret1 = $cache->deferCall('set', ['name1', 'swoft1']); -// return cache('cacheKey'); - return 111; - } - - /** - * Mysql task - * - * @return array - */ - public function mysql(){ - $result = User::findById(4212)->getResult(); - - $query = User::findById(4212); - - /* @var User $user */ - $user = $query->getResult(User::class); - return [$result, $user->getName()]; - } - - /** - * Http task - * - * @return mixed - */ - public function http() - { - $client = new Client(); - $response = $client->get('http://www.swoft.org/')->getResponse()->getBody()->getContents(); - $response2 = $client->get('http://127.0.0.1/redis/testCache')->getResponse()->getBody()->getContents(); - - $data['result1'] = $response; - $data['result2'] = $response2; - return $data; - } - - public function console(string $data) - { - var_dump('console', $data); - return ['console']; - } - - /** - * Rpc task - * - * @return mixed - */ - public function rpc() - { - $user = $this->demoService->getUser('6666'); - $defer1 = $this->demoService->deferGetUser('666'); - $defer2 = $this->demoService->deferGetUser('888'); - - $result1 = $defer1->getResult(); - $result2 = $defer2->getResult(); - return [$user, $result1, $result2]; - } - - /** - * Rpc task - * - * @return mixed - */ - public function rpc2() - { - return $this->logic->rpcCall(); - } - - public function batchTask(){ - sleep(mt_rand(1, 2)); - - /* @var User $user*/ - $user = User::findById(80368)->getResult(); - return $user->toJson(); - } - - /** - * crontab定时任务 - * 每一秒执行一次 - * - * @Scheduled(cron="* * * * * *") - */ - public function cronTask() - { - echo time() . "每一秒执行一次 \n"; - return 'cron'; - } - - /** - * 每分钟第3-5秒执行 - * - * @Scheduled(cron="3-5 * * * * *") - */ - public function cronooTask() - { - echo time() . "第3-5秒执行\n"; - return 'cron'; - } -} diff --git a/app/WebSocket/EchoController.php b/app/WebSocket/EchoController.php deleted file mode 100644 index 867903ce..00000000 --- a/app/WebSocket/EchoController.php +++ /dev/null @@ -1,54 +0,0 @@ -push($fd, 'hello, welcome! :)'); - } - - /** - * @param Server $server - * @param Frame $frame - */ - public function onMessage(Server $server, Frame $frame) - { - $server->push($frame->fd, 'hello, I have received your message: ' . $frame->data); - } - - /** - * @param Server $server - * @param int $fd - */ - public function onClose(Server $server, int $fd) - { - // do something. eg. record log, unbind user ... - } -} diff --git a/bin/bootstrap.php b/bin/bootstrap.php deleted file mode 100644 index 9b1a4183..00000000 --- a/bin/bootstrap.php +++ /dev/null @@ -1,19 +0,0 @@ -bootstrap(); diff --git a/bin/swoft b/bin/swoft deleted file mode 100644 index 10442e4c..00000000 --- a/bin/swoft +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env php -run(); \ No newline at end of file diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 7ea1dddc..00000000 --- a/changelog.md +++ /dev/null @@ -1,108 +0,0 @@ -# Change log - -# 2018-08-15 -* 增加自定义组件支持 - -# 2018-08-07 -* 适配 Swoole 4.0.3 协程API变更 -* 加强 RpcClient 的自定义配置的能力 -* 完善 Redis 的命令支持 - -# 2018-04-05 - -* 添加 websocket 支持 -* 完成基本的 devtool - -# 2018-03-05 -* 组件化拆分 - -# 2018-01-05 - -* 重构HttpClient -* 重构Redis -* 创建实体新增特殊变量别名 - -# 2017-12-26 - -* 重构连接池 - -# 2017-12-20 - -* 新增匹配事件 - -# 2017-12-16 - -* 重构热更新兼容跨平台 - -# 2017-12-11 - -* 事件管理改动,使用 psr 14 实现 - -# 2017-12-10 - -* 新增HTTP、RPC验证器 -* 新增HTTP、RPC中间件 - -# 2017-12-2 - -* 新增RESTful风格兼容 - -# 2017-11-29 - -* 重构请求流程 -* 简化控制器和RPC服务操作 -* 增加 创建实体 操作 - -# 2017-11-15 - -* 增加 Pipeline 组件 - -# 2017-11-13 - -* 增加 PHPUnit 单元测试 - -# 2017-11-12 - -* 根据 Psr-7 重构 Request/Response - -# 2017-11-02 - -* 重构 config 配置 -* 新增 .env 配置环境信息 - -# 2017-11-01 - -* 新增定时任务 - -# 2017-10-24 - -* 协程、异步任务投递 -* 自定义用户进程 -* RPC, Redis, Http, Mysql 协程和同步客户端无缝切换 -* HTTP 和 RPC 服务器分开管理 - -# 2017-09-19 - -* 数据库 ORM - -# 2017-09-02 - -* 别名机制 -* 事件机制 -* 国际化(i18n) -* 命名空间统一大写。 - -# 2017-08-28 -* 新增 Inotify 自动 Reload - -# 2017-08-24 - -* 重写 IoC 容器 -* 新增控制器路由注解注册 -* 重写容器注入,不再依赖 PHP-DI - -# 2017-08-15 - -* 重构 Console 命令行 - -# ...... diff --git a/composer.json b/composer.json deleted file mode 100644 index df36edab..00000000 --- a/composer.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", - "license": "Apache-2.0", - "require": { - "php": ">=7.0", - "ext-swoole": ">=2.1", - "swoft/framework": "^1.0", - "swoft/rpc": "^1.0", - "swoft/rpc-server": "^1.0", - "swoft/rpc-client": "^1.0", - "swoft/http-server": "^1.0", - "swoft/http-client": "^1.0", - "swoft/websocket-server": "^1.0", - "swoft/task": "^1.0", - "swoft/http-message": "^1.0", - "swoft/view": "^1.0", - "swoft/db": "^1.1", - "swoft/cache": "^1.0", - "swoft/redis": "^1.0", - "swoft/console": "^1.0", - "swoft/session": "^1.0", - "swoft/i18n": "^1.0", - "swoft/process": "^1.0", - "swoft/memory": "^1.0", - "swoft/service-governance": "^1.0" - }, - "require-dev": { - "swoft/swoole-ide-helper": "dev-master", - "swoft/devtool": "^1.0", - "phpunit/phpunit": "^5.7", - "friendsofphp/php-cs-fixer": "^2.10", - "psy/psysh": "@stable" - }, - "autoload": { - "psr-4": { - "App\\": "app/" - }, - "files": [ - "app/Swoft.php", - "app/Helper/Functions.php" - ] - }, - "autoload-dev": { - "psr-4": { - "Swoft\\Test\\": "test/" - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "test": "./vendor/bin/phpunit -c phpunit.xml", - "cs-fix": "./vendor/bin/php-cs-fixer fix $1" - }, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - } - } -} diff --git a/config/beans/base.php b/config/beans/base.php deleted file mode 100644 index 8711d971..00000000 --- a/config/beans/base.php +++ /dev/null @@ -1,38 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'serverDispatcher' => [ - 'middlewares' => [ - \Swoft\View\Middleware\ViewMiddleware::class, - // \Swoft\Devtool\Middleware\DevToolMiddleware::class, - // \Swoft\Session\Middleware\SessionMiddleware::class, - ] - ], - 'httpRouter' => [ - 'ignoreLastSlash' => false, - 'tmpCacheNumber' => 1000, - 'matchAll' => '', - ], - 'requestParser' => [ - 'parsers' => [ - - ], - ], - 'view' => [ - 'viewsPath' => '@resources/views/', - ], - 'cache' => [ - 'driver' => 'redis', - ], - 'demoRedis' => [ - 'class' => \Swoft\Redis\Redis::class, - 'poolName' => 'demoRedis' - ] -]; diff --git a/config/beans/console.php b/config/beans/console.php deleted file mode 100644 index d7acd21c..00000000 --- a/config/beans/console.php +++ /dev/null @@ -1,12 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - -]; \ No newline at end of file diff --git a/config/beans/log.php b/config/beans/log.php deleted file mode 100644 index 06cac847..00000000 --- a/config/beans/log.php +++ /dev/null @@ -1,40 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'noticeHandler' => [ - 'class' => \Swoft\Log\FileHandler::class, - 'logFile' => '@runtime/logs/notice.log', - 'formatter' => '${lineFormatter}', - 'levels' => [ - \Swoft\Log\Logger::NOTICE, - \Swoft\Log\Logger::INFO, - \Swoft\Log\Logger::DEBUG, - \Swoft\Log\Logger::TRACE, - ], - ], - 'applicationHandler' => [ - 'class' => \Swoft\Log\FileHandler::class, - 'logFile' => '@runtime/logs/error.log', - 'formatter' => '${lineFormatter}', - 'levels' => [ - \Swoft\Log\Logger::ERROR, - \Swoft\Log\Logger::WARNING, - ], - ], - 'logger' => [ - 'name' => APP_NAME, - 'enable' => env('LOG_ENABLE', false), - 'flushInterval' => 100, - 'flushRequest' => true, - 'handlers' => [ - '${noticeHandler}', - '${applicationHandler}', - ], - ], -]; diff --git a/config/beans/service.php b/config/beans/service.php deleted file mode 100644 index cfbf3dd2..00000000 --- a/config/beans/service.php +++ /dev/null @@ -1,11 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ -]; \ No newline at end of file diff --git a/config/define.php b/config/define.php deleted file mode 100644 index 2a9a32d2..00000000 --- a/config/define.php +++ /dev/null @@ -1,32 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -! defined('DS') && define('DS', DIRECTORY_SEPARATOR); -// App name -! defined('APP_NAME') && define('APP_NAME', 'swoft'); -// Project base path -! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1)); - -// Register alias -$aliases = [ - '@root' => BASE_PATH, - '@env' => '@root', - '@app' => '@root/app', - '@res' => '@root/resources', - '@runtime' => '@root/runtime', - '@configs' => '@root/config', - '@resources' => '@root/resources', - '@beans' => '@configs/beans', - '@properties' => '@configs/properties', - '@console' => '@beans/console.php', - '@commands' => '@app/command', - '@vendor' => '@root/vendor', -]; - -\Swoft\App::setAliases($aliases); diff --git a/config/properties/app.php b/config/properties/app.php deleted file mode 100644 index 605d0d87..00000000 --- a/config/properties/app.php +++ /dev/null @@ -1,30 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'env' => env('APP_ENV', 'test'), - 'debug' => env('APP_DEBUG', false), - 'version' => '1.0', - 'autoInitBean' => true, - 'bootScan' => [ - 'App\Commands', - 'App\Boot', - ], - 'excludeScan' => [ - 'App\Helper', - ], - 'translator' => [ - 'languageDir' => '@resources/languages/', - ], - 'db' => require __DIR__ . DS . 'db.php', - 'cache' => require __DIR__ . DS . 'cache.php', - 'service' => require __DIR__ . DS . 'service.php', - 'breaker' => require __DIR__ . DS . 'breaker.php', - 'provider' => require __DIR__ . DS . 'provider.php', -]; diff --git a/config/properties/breaker.php b/config/properties/breaker.php deleted file mode 100644 index b9f1dd2e..00000000 --- a/config/properties/breaker.php +++ /dev/null @@ -1,16 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'user' => [ - 'failCount' => 3, - 'successCount' => 3, - 'delayTime' => 500, - ], -]; \ No newline at end of file diff --git a/config/properties/cache.php b/config/properties/cache.php deleted file mode 100644 index 28328aff..00000000 --- a/config/properties/cache.php +++ /dev/null @@ -1,31 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'redis' => [ - 'name' => 'redis', - 'uri' => [ - '127.0.0.1:6379', - '127.0.0.1:6379', - ], - 'minActive' => 8, - 'maxActive' => 8, - 'maxWait' => 8, - 'maxWaitTime' => 3, - 'maxIdleTime' => 60, - 'timeout' => 8, - 'db' => 1, - 'prefix' => 'redis_', - 'serialize' => 0, - ], - 'demoRedis' => [ - 'db' => 2, - 'prefix' => 'demo_redis_', - ], -]; \ No newline at end of file diff --git a/config/properties/db.php b/config/properties/db.php deleted file mode 100644 index 662b493d..00000000 --- a/config/properties/db.php +++ /dev/null @@ -1,38 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'master' => [ - 'name' => 'master', - 'uri' => [ - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8', - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8', - ], - 'minActive' => 8, - 'maxActive' => 8, - 'maxWait' => 8, - 'timeout' => 8, - 'maxIdleTime' => 60, - 'maxWaitTime' => 3, - ], - - 'slave' => [ - 'name' => 'slave', - 'uri' => [ - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8', - '127.0.0.1:3306/test?user=root&password=123456&charset=utf8', - ], - 'minActive' => 8, - 'maxActive' => 8, - 'maxWait' => 8, - 'timeout' => 8, - 'maxIdleTime' => 60, - 'maxWaitTime' => 3, - ], -]; \ No newline at end of file diff --git a/config/properties/provider.php b/config/properties/provider.php deleted file mode 100644 index efbf716b..00000000 --- a/config/properties/provider.php +++ /dev/null @@ -1,39 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'consul' => [ - 'address' => '', - 'port' => 8500, - 'register' => [ - 'id' => '', - 'name' => '', - 'tags' => [], - 'enableTagOverride' => false, - 'service' => [ - 'address' => 'localhost', - 'port' => '8099', - ], - 'check' => [ - 'id' => '', - 'name' => '', - 'tcp' => 'localhost:8099', - 'interval' => 10, - 'timeout' => 1, - ], - ], - 'discovery' => [ - 'name' => 'user', - 'dc' => 'dc', - 'near' => '', - 'tag' =>'', - 'passing' => true - ] - ], -]; \ No newline at end of file diff --git a/config/properties/service.php b/config/properties/service.php deleted file mode 100644 index 60b23b57..00000000 --- a/config/properties/service.php +++ /dev/null @@ -1,27 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'user' => [ - 'name' => 'redis', - 'uri' => [ - '127.0.0.1:8099', - '127.0.0.1:8099', - ], - 'minActive' => 8, - 'maxActive' => 8, - 'maxWait' => 8, - 'maxWaitTime' => 3, - 'maxIdleTime' => 60, - 'timeout' => 8, - 'useProvider' => false, - 'balancer' => 'random', - 'provider' => 'consul', - ] -]; \ No newline at end of file diff --git a/config/server.php b/config/server.php deleted file mode 100644 index f399161f..00000000 --- a/config/server.php +++ /dev/null @@ -1,68 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'server' => [ - 'pfile' => alias(env('PFILE', '@runtime/swoft.pid')), - 'pname' => env('PNAME', 'php-swoft'), - 'tcpable' => env('TCPABLE', true), - 'cronable' => env('CRONABLE', false), - 'autoReload' => env('AUTO_RELOAD', true), - ], - 'tcp' => [ - 'host' => env('TCP_HOST', '0.0.0.0'), - 'port' => env('TCP_PORT', 8099), - 'mode' => env('TCP_MODE', SWOOLE_PROCESS), - 'type' => env('TCP_TYPE', SWOOLE_SOCK_TCP), - 'package_max_length' => env('TCP_PACKAGE_MAX_LENGTH', 2048), - 'open_eof_check' => env('TCP_OPEN_EOF_CHECK', false), - 'open_eof_split' => env('TCP_OPEN_EOF_SPLIT', true), - 'package_eof' => "\r\n", - 'client' => [ - 'package_max_length' => env('TCP_CLIENT_PACKAGE_MAX_LENGTH', 1024 * 1024 * 2), - 'open_eof_check' => env('TCP_CLIENT_OPEN_EOF_CHECK', false), - 'open_eof_split' => env('TCP_CLIENT_OPEN_EOF_SPLIT', true), - 'package_eof' => "\r\n", - ], - ], - 'http' => [ - 'host' => env('HTTP_HOST', '0.0.0.0'), - 'port' => env('HTTP_PORT', 80), - 'mode' => env('HTTP_MODE', SWOOLE_PROCESS), - 'type' => env('HTTP_TYPE', SWOOLE_SOCK_TCP), - ], - 'ws' => [ - // enable handle http request ? - 'enable_http' => env('WS_ENABLE_HTTP', true), - // other settings will extend the 'http' config - // you can define separately to overwrite existing settings - ], - 'crontab' => [ - 'task_count' => env('CRONTAB_TASK_COUNT', 1024), - 'task_queue' => env('CRONTAB_TASK_QUEUE', 2048), - ], - 'setting' => [ - 'worker_num' => env('WORKER_NUM', 1), - 'max_request' => env('MAX_REQUEST', 10000), - 'daemonize' => env('DAEMONIZE', 0), - 'dispatch_mode' => env('DISPATCH_MODE', 2), - 'log_file' => env('LOG_FILE', '@runtime/logs/swoole.log'), - 'task_worker_num' => env('TASK_WORKER_NUM', 1), - 'package_max_length' => env('PACKAGE_MAX_LENGTH', 2048), - 'upload_tmp_dir' => env('UPLOAD_TMP_DIR', '@runtime/uploadfiles'), - 'document_root' => env('DOCUMENT_ROOT', BASE_PATH . '/public'), - 'enable_static_handler' => env('ENABLE_STATIC_HANDLER', false), - 'open_http2_protocol' => env('OPEN_HTTP2_PROTOCOL', false), - 'ssl_cert_file' => env('SSL_CERT_FILE', ''), - 'ssl_key_file' => env('SSL_KEY_FILE', ''), - 'task_ipc_mode' => env('TASK_IPC_MODE', 1), - 'message_queue_key' => env('MESSAGE_QUEUE_KEY', 0x70001001), - 'task_tmpdir' => env('TASK_TMPDIR', '/tmp'), - ], -]; diff --git a/dev.composer.json b/dev.composer.json deleted file mode 100644 index c5b9cea1..00000000 --- a/dev.composer.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole 2", - "license": "Apache-2.0", - "require": { - "php": ">=7.0", - "swoft/framework": "^1.0", - "swoft/rpc": "^1.0", - "swoft/rpc-server": "^1.0", - "swoft/rpc-client": "^1.0", - "swoft/http-server": "^1.0", - "swoft/http-client": "^1.0", - "swoft/websocket-server": "^1.0", - "swoft/task": "^1.0", - "swoft/http-message": "^1.0", - "swoft/view": "^1.0", - "swoft/db": "^1.0", - "swoft/cache": "^1.0", - "swoft/redis": "^1.0", - "swoft/console": "^1.0", - "swoft/devtool": "^1.0", - "swoft/session": "^1.0", - "swoft/i18n": "^1.0", - "swoft/process": "^1.0", - "swoft/memory": "^1.0", - "swoft/service-governance": "^1.0", - "swoft/component": "dev-master as 1.0" - }, - "require-dev": { - "swoft/swoole-ide-helper": "2.1.3", - "phpunit/phpunit": "^5.7", - "friendsofphp/php-cs-fixer": "^2.10", - "psy/psysh": "@stable" - }, - "autoload": { - "psr-4": { - "App\\": "app/" - }, - "files": [ - "app/Swoft.php", - "app/Helper/Functions.php" - ] - }, - "autoload-dev": { - "psr-4": { - "Swoft\\Test\\": "test/" - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "test": "./vendor/bin/phpunit -c phpunit.xml", - "cs-fix": "./vendor/bin/php-cs-fixer fix $1" - }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-component.git" - }, - { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - } - ] -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index c912a751..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -version: '3' - -services: - swoft: - image: swoft/swoft:latest -# build: ./ - ports: - - "80:80" - volumes: - - ./:/var/www/swoft - stdin_open: true - tty: true - privileged: true - entrypoint: ["php", "/var/www/swoft/bin/swoft", "start"] diff --git a/phar.build.inc b/phar.build.inc deleted file mode 100644 index 31640221..00000000 --- a/phar.build.inc +++ /dev/null @@ -1,41 +0,0 @@ -stripComments(true) - ->setShebang(true) - ->addSuffix('.json')// for add composer.json - ->addExclude([ - 'test', - 'tests', - 'runtime', - 'eaglewu', - ]) - ->addFile([ - 'LICENSE', - 'README.md', - ]) - ->setCliIndex('bin/swoft') - // ->setWebIndex('web/index.php') - // ->setVersionFile('config/config.php') -; - -// Command Controller 命令类不去除注释,注释上是命令帮助信息 -$compiler->setStripFilter(function ($file) { - /** @var \SplFileInfo $file */ - $path = $file->getPath(); - - if (strpos($path, 'swoft')) { - return false; - } - - return false === strpos($file->getFilename(), 'Command.php'); -}); diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index 2e5c0390..00000000 --- a/phpunit.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - ./test - - - - - ./app - - - diff --git a/public/.gitkeep b/public/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/resources/README.md b/resources/README.md deleted file mode 100644 index 3e92d63d..00000000 --- a/resources/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# 资源目录 - -- views - 视图资源 -- languages - 翻译资源 diff --git a/resources/languages/en/default.php b/resources/languages/en/default.php deleted file mode 100644 index ac27d49b..00000000 --- a/resources/languages/en/default.php +++ /dev/null @@ -1,12 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'title' => 'en title' -]; diff --git a/resources/languages/en/msg.php b/resources/languages/en/msg.php deleted file mode 100644 index 4f09cc56..00000000 --- a/resources/languages/en/msg.php +++ /dev/null @@ -1,12 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'body' => 'this is msg [%s] %d', -]; diff --git a/resources/languages/zh/default.php b/resources/languages/zh/default.php deleted file mode 100644 index c0cccbae..00000000 --- a/resources/languages/zh/default.php +++ /dev/null @@ -1,12 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'title' => '中文标题' -]; diff --git a/resources/languages/zh/msg.php b/resources/languages/zh/msg.php deleted file mode 100644 index 9a3d7bff..00000000 --- a/resources/languages/zh/msg.php +++ /dev/null @@ -1,12 +0,0 @@ - - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -return [ - 'body' => '这是一条消息 [%s] %d', -]; diff --git a/resources/views/demo/content.php b/resources/views/demo/content.php deleted file mode 100644 index 8771a66c..00000000 --- a/resources/views/demo/content.php +++ /dev/null @@ -1,32 +0,0 @@ -
    -
    -
    -

    Swoft framework

    -

    - swoft 是基于swoole协程2.x的高性能PHP微服务框架,内置http,rpc 服务器。框架全协程实现,性能优于传统的php-fpm模式。 -

    -
    -

    更多信息请访问 github 或者 官方文档.

    -

    - Github - Document -

    -
    -
    -

    使用布局文件

    -
    -view file: 
    -
    -layout file: 
    -
    -view method: 
    -
    -
    -使用布局文件, 方式有两种:
    -
    -1. 在配置中 配置默认的布局文件,那这里即使不设置 layout, 也会使用默认的
    -2. 如这里一样,手动设置一个布局文件。它的优先级更高(即使有默认的布局文件,也会使用当前传入的替代。)
    -    
    -
    -
    -
    \ No newline at end of file diff --git a/resources/views/demo/view.php b/resources/views/demo/view.php deleted file mode 100644 index a8453b47..00000000 --- a/resources/views/demo/view.php +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - Document - - - -
    - -
    -
    -
    -
    -
    -
    -

    Fluid jumbotron

    -

    This is a modified jumbotron that occupies the entire horizontal space of its parent.

    -
    -
    -

    没有使用 layout

    -
    -view file: 
    -
    -view method: 
    -      
    -
    -
    -
    - - \ No newline at end of file diff --git a/resources/views/exception/index.php b/resources/views/exception/index.php deleted file mode 100644 index 82f8ce7e..00000000 --- a/resources/views/exception/index.php +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - <?= $name ?> - - - - - - - - -
    - -
    -
    - -
    - - -
    - -
    - - - -
    -
    - - \ No newline at end of file diff --git a/resources/views/index/index.php b/resources/views/index/index.php deleted file mode 100644 index b74e6744..00000000 --- a/resources/views/index/index.php +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - - <?= $name ?> - - - - - - - - -
    - -
    -
    - -
    - - -
    - -
    - - - -
    -
    - - \ No newline at end of file diff --git a/resources/views/layouts/default.php b/resources/views/layouts/default.php deleted file mode 100644 index 866e894f..00000000 --- a/resources/views/layouts/default.php +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - Demo for layout - - - - include('layouts/default/header') ?> - -
    - -
    {_CONTENT_}
    - include('layouts/default/footer') ?> -
    - - \ No newline at end of file diff --git a/resources/views/layouts/default/footer.php b/resources/views/layouts/default/footer.php deleted file mode 100644 index fb5ba8b8..00000000 --- a/resources/views/layouts/default/footer.php +++ /dev/null @@ -1,20 +0,0 @@ - \ No newline at end of file diff --git a/resources/views/layouts/default/header.php b/resources/views/layouts/default/header.php deleted file mode 100644 index 4760e87f..00000000 --- a/resources/views/layouts/default/header.php +++ /dev/null @@ -1,29 +0,0 @@ -
    - -
    \ No newline at end of file diff --git a/runtime/logs/.gitkeep b/runtime/logs/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/runtime/uploadfiles/.gitkeep b/runtime/uploadfiles/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/Cases/AbstractTestCase.php b/test/Cases/AbstractTestCase.php deleted file mode 100644 index 848ac0dd..00000000 --- a/test/Cases/AbstractTestCase.php +++ /dev/null @@ -1,187 +0,0 @@ -buildMockRequest($method, $uri, $parameters, $accept, $swooleRequest, $headers); - - $swooleRequest->setRawContent($rawContent); - - $request = Request::loadFromSwooleRequest($swooleRequest); - $response = new Response($swooleResponse); - - /** @var \Swoft\Http\Server\ServerDispatcher $dispatcher */ - $dispatcher = App::getBean('serverDispatcher'); - return $dispatcher->dispatch($request, $response); - } - - /** - * Send a mock json request - * - * @param string $method - * @param string $uri - * @param array $parameters - * @param array $headers - * @param string $rawContent - * @return bool|\Swoft\Http\Message\Testing\Web\Response - */ - public function json( - string $method, - string $uri, - array $parameters = [], - array $headers = [], - string $rawContent = '' - ) { - return $this->request($method, $uri, $parameters, self::ACCEPT_JSON, $headers, $rawContent); - } - - /** - * Send a mock view request - * - * @param string $method - * @param string $uri - * @param array $parameters - * @param array $headers - * @param string $rawContent - * @return bool|\Swoft\Http\Message\Testing\Web\Response - */ - public function view( - string $method, - string $uri, - array $parameters = [], - array $headers = [], - string $rawContent = '' - ) { - return $this->request($method, $uri, $parameters, self::ACCEPT_VIEW, $headers, $rawContent); - } - - /** - * Send a mock raw content request - * - * @param string $method - * @param string $uri - * @param array $parameters - * @param array $headers - * @param string $rawContent - * @return bool|\Swoft\Http\Message\Testing\Web\Response - */ - public function raw( - string $method, - string $uri, - array $parameters = [], - array $headers = [], - string $rawContent = '' - ) { - return $this->request($method, $uri, $parameters, self::ACCEPT_RAW, $headers, $rawContent); - } - - /** - * @param string $method - * @param string $uri - * @param array $parameters - * @param string $accept - * @param \Swoole\Http\Request $swooleRequest - * @param array $headers - */ - protected function buildMockRequest( - string $method, - string $uri, - array $parameters, - string $accept, - &$swooleRequest, - array $headers = [] - ) { - $urlAry = parse_url(http://github.com/$uri); - $urlParams = []; - if (isset($urlAry['query'])) { - parse_str($urlAry['query'], $urlParams); - } - $defaultHeaders = [ - 'host' => '127.0.0.1', - 'connection' => 'keep-alive', - 'cache-control' => 'max-age=0', - 'user-agent' => 'PHPUnit', - 'upgrade-insecure-requests' => '1', - 'accept' => $accept, - 'dnt' => '1', - 'accept-encoding' => 'gzip, deflate, br', - 'accept-language' => 'zh-CN,zh;q=0.8,en;q=0.6,it-IT;q=0.4,it;q=0.2', - ]; - - $swooleRequest->fd = 1; - $swooleRequest->header = ArrayHelper::merge($headers, $defaultHeaders); - $swooleRequest->server = [ - 'request_method' => $method, - 'request_uri' => $uri, - 'path_info' => '/', - 'request_time' => microtime(), - 'request_time_float' => microtime(true), - 'server_port' => 80, - 'remote_port' => 54235, - 'remote_addr' => '10.0.2.2', - 'master_time' => microtime(), - 'server_protocol' => 'HTTP/1.1', - 'server_software' => 'swoole-http-server', - ]; - - if ($method == 'GET') { - $swooleRequest->get = $parameters; - } elseif ($method == 'POST') { - $swooleRequest->post = $parameters; - } - - if (! empty($urlParams)) { - $get = empty($swooleRequest->get) ? [] : $swooleRequest->get; - $swooleRequest->get = array_merge($urlParams, $get); - } - } -} diff --git a/test/Cases/DemoControllerTest.php b/test/Cases/DemoControllerTest.php deleted file mode 100644 index b5b97772..00000000 --- a/test/Cases/DemoControllerTest.php +++ /dev/null @@ -1,53 +0,0 @@ - - * @copyright Copyright 2010-2017 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class DemoControllerTest extends AbstractTestCase -{ - - /** - * @test - * @covers \App\Controllers\DemoController - */ - public function actionView() - { - $response = $this->request('GET', '/demo2/view', [], parent::ACCEPT_VIEW); - $response->assertSuccessful()->assertSee('Swoft')->assertSee('没有使用 layout'); - } - - /** - * @test - * @covers \App\Controllers\DemoController - */ - public function actionLayout() - { - $response = $this->request('GET', '/demo2/layout', [], parent::ACCEPT_VIEW); - $response->assertSuccessful()->assertSee('Swoft')->assertSee('使用布局文件'); - } - - /** - * @test - */ - public function actionI18n() - { - $response = $this->request('GET', '/demo2/i18n', [], parent::ACCEPT_VIEW); - $response->assertSuccessful()->assertSee('title'); - } - -} \ No newline at end of file diff --git a/test/Cases/IndexControllerTest.php b/test/Cases/IndexControllerTest.php deleted file mode 100644 index 4aca2fd1..00000000 --- a/test/Cases/IndexControllerTest.php +++ /dev/null @@ -1,131 +0,0 @@ - - * @copyright Copyright 2010-2017 Swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class IndexControllerTest extends AbstractTestCase -{ - - /** - * @test - * @covers \App\Controllers\IndexController - */ - public function testIndex() - { - $expectedResult = [ - 'name' => 'Swoft', - 'notes' => [ - 'New Generation of PHP Framework', - 'High Performance, Coroutine and Full Stack' - ], - 'links' => [ - [ - 'name' => 'Home', - 'link' => 'http://www.swoft.org/', - ], - [ - 'name' => 'Documentation', - 'link' => 'http://doc.swoft.org/', - ], - [ - 'name' => 'Case', - 'link' => 'http://swoft.org/case', - ], - [ - 'name' => 'Issue', - 'link' => 'https://github.com/swoft-cloud/swoft/issues', - ], - [ - 'name' => 'GitHub', - 'link' => 'https://github.com/swoft-cloud/swoft', - ], - ] - ]; - - $jsonAssert = function ($response) use ($expectedResult) { - $this->assertInstanceOf(Response::class, $response); - /** @var Response $response */ - $response->assertSuccessful() - ->assertHeaderContain('Content-Type', 'application/json') - ->assertSee('Swoft') - ->assertSeeText('New Generation of PHP Framework') - ->assertDontSee('Swoole') - ->assertDontSeeText('Swoole') - ->assertJson(['name' => 'Swoft']) - ->assertExactJson($expectedResult) - ->assertJsonFragment(['name' => 'Home']) - ->assertJsonMissing(['name' => 'Swoole']) - ->assertJsonStructure(['name', 'notes']); - }; - // Json model - $response = $this->request('GET', '/', [], parent::ACCEPT_JSON); - $response->assertHeaderContain('Content-Type', parent::ACCEPT_JSON); - $jsonAssert($response); - - // Raw model - $response = $this->request('GET', '/', [], parent::ACCEPT_RAW); - $response->assertHeaderContain('Content-Type', parent::ACCEPT_JSON); - $jsonAssert($response); - - // View model - $response = $this->request('GET', '/', [], parent::ACCEPT_VIEW); - $response->assertSuccessful() - ->assertSee($expectedResult['name']) - ->assertSee($expectedResult['notes'][0]) - ->assertSee($expectedResult['notes'][1]) - ->assertHeaderContain('Content-Type', 'text/html'); - - // absolutePath - $response = $this->request('GET', '/index/absolutePath', [], parent::ACCEPT_VIEW); - $response->assertSuccessful() - ->assertSee($expectedResult['name']) - ->assertSee($expectedResult['notes'][0]) - ->assertSee($expectedResult['notes'][1]) - ->assertHeader('Content-Type', 'text/html'); - } - - /** - * @test - * @covers \App\Controllers\IndexController - */ - public function testException() - { - $data = [ - 'message' => 'bad request exception' - ]; - - $response = $this->request('GET', '/index/exception', [], parent::ACCEPT_JSON); - $response->assertJson($data); - } - - /** - * @test - * @covers \App\Controllers\IndexController - */ - public function testRaw() - { - $expected = 'Swoft'; - $response = $this->request('GET', '/index/raw', [], parent::ACCEPT_RAW); - $response->assertSuccessful()->assertSee($expected); - - $response = $this->request('GET', '/index/raw', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson(['data' => $expected]); - } - -} diff --git a/test/Cases/MiddlewareTest.php b/test/Cases/MiddlewareTest.php deleted file mode 100644 index 7f58d35b..00000000 --- a/test/Cases/MiddlewareTest.php +++ /dev/null @@ -1,55 +0,0 @@ -request('GET', '/middleware/action1', [], parent::ACCEPT_JSON); - $response->assertExactJson(['middleware']); - $response->assertHeader('Middleware-Group-Test', 'success'); - $response->assertHeader('Sub-Middleware-Test', 'success'); - $response->assertHeader('Middleware-Action-Test', 'success'); - } - - /** - * @covers \App\Controllers\MiddlewareController::action2 - * @test - */ - public function action2() - { - $response = $this->request('GET', '/middleware/action2', [], parent::ACCEPT_JSON); - $response->assertExactJson(['middleware2']); - $response->assertHeader('Middleware-Group-Test', 'success'); - $response->assertHeader('Sub-Middleware-Test', 'success'); - $response->assertHeader('Middleware-Action-Test', 'success'); - } - - /** - * @covers \App\Controllers\MiddlewareController::action3 - * @test - */ - public function action3() - { - $response = $this->request('GET', '/middleware/action3', [], parent::ACCEPT_JSON); - $response->assertExactJson(['middleware3']); - $response->assertHeader('Controller-Test-Middleware', 'success'); - $response->assertHeader('Controller-Sub-Middleware', 'success'); - } -} \ No newline at end of file diff --git a/test/Cases/RedisControllerTest.php b/test/Cases/RedisControllerTest.php deleted file mode 100644 index 3a70d92c..00000000 --- a/test/Cases/RedisControllerTest.php +++ /dev/null @@ -1,195 +0,0 @@ -has('test'); - $this->isRedisConnected = true; - } catch (\Exception $e) { - // No connection or else error - } - } - - /** - * @param \Closure $closure - */ - protected function runRedisTest(\Closure $closure) - { - if ($this->isRedisConnected) { - $closure(); - } else { - $this->markTestSkipped('No redis connection'); - } - } - - /** - * @test - * @requires extension redis - */ - public function cache() - { - $this->runRedisTest(function () { - $expected = [ - true, - 'swoft', - ]; - $response = $this->request('GET', '/redis/testCache', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function redis() - { - $this->runRedisTest(function () { - $expected = [ - true, - 'swoft2', - ]; - $response = $this->request('GET', '/redis/testRedis', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function func() - { - $this->runRedisTest(function () { - $expected = [ - true, - 'swoft3', - ]; - $response = $this->request('GET', '/redis/testFunc', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function func2() - { - $this->runRedisTest(function () { - $expected = [ - true, - 'swoft3', - 'value3', - ]; - $response = $this->request('GET', '/redis/testFunc2', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function delete() - { - $this->runRedisTest(function () { - $expected = [ - true, - 1, - ]; - $response = $this->request('GET', '/redis/testDelete', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function clear() - { - $this->runRedisTest(function () { - $expected = [ - true, - ]; - $response = $this->request('GET', '/redis/clear', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function multiple() - { - $this->runRedisTest(function () { - $expected = [ - true, - [ - 'name6' => 'swoft6', - 'name8' => 'swoft8', - ], - ]; - $response = $this->request('GET', '/redis/setMultiple', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function deleteMultiple() - { - $this->runRedisTest(function () { - $expected = [ - true, - 2, - ]; - $response = $this->request('GET', '/redis/deleteMultiple', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } - - /** - * @test - * @requires extension redis - */ - public function has() - { - $this->runRedisTest(function () { - $expected = [ - true, - true, - ]; - $response = $this->request('GET', '/redis/has', [], parent::ACCEPT_JSON); - $response->assertSuccessful()->assertJson($expected); - }); - } -} \ No newline at end of file diff --git a/test/Cases/RestTest.php b/test/Cases/RestTest.php deleted file mode 100644 index cefdbbe7..00000000 --- a/test/Cases/RestTest.php +++ /dev/null @@ -1,109 +0,0 @@ - - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class RestTest extends AbstractTestCase -{ - /** - * @covers \App\Controllers\RestController@list - */ - public function testList() - { - $data = ['list']; - $response = $this->request('GET', '/user', [], parent::ACCEPT_JSON); - $response->assertExactJson($data); - } - - /** - * @covers \App\Controllers\RestController@create - */ - public function testCreate() - { - $data = ['create', 'stelin']; - $response = $this->request('POST', '/user', ['name' => 'stelin'], parent::ACCEPT_JSON); - $response->assertExactJson($data); - - $headers = [ - 'Content-Type' => 'application/json' - ]; - $content = '{"name":"stelin","age":18,"desc":"swoft framework"}'; - $data = [ - 'name' => 'stelin', - 'age' => 18, - 'desc' => 'swoft framework', - ]; - $response = $this->request('PUT', '/user', [], parent::ACCEPT_JSON, $headers, $content); - $response->assertExactJson($data); - } - - /** - * @covers \App\Controllers\RestController@getUser - */ - public function testGetUser() - { - $data = ['getUser',123]; - $response = $this->request('GET', '/user/123', [], parent::ACCEPT_JSON); - $response->assertExactJson($data); - } - - /** - * @covers \App\Controllers\RestController@getBookFromUser - */ - public function testGetBookFromUser() - { - $data = ['bookFromUser',123, '456']; - $response = $this->request('GET', '/user/123/book/456', [], parent::ACCEPT_JSON); - $response->assertExactJson($data); - } - - /** - * @covers \App\Controllers\RestController@deleteUser - */ - public function testDeleteUser() - { - $data = ['delete',123]; - $response = $this->request('DELETE', '/user/123', [], parent::ACCEPT_JSON); - $response->assertExactJson($data); - } - - /** - * @covers \App\Controllers\RestController@updateUser - */ - public function testUpdateUser() - { - $headers = [ - 'Content-Type' => 'application/json' - ]; - $content = '{"name":"stelin","age":18,"desc":"swoft framework"}'; - $data = [ - 'name' => 'stelin', - 'age' => 18, - 'desc' => 'swoft framework', - 'update' => 'update', - 'uid' => 123 - ]; - - $response = $this->request('PUT', '/user/123', [], parent::ACCEPT_JSON, $headers, $content); - $response->assertExactJson($data); - - $response = $this->request('PATCH', '/user/123', [], parent::ACCEPT_JSON, $headers, $content); - $response->assertExactJson($data); - } -} \ No newline at end of file diff --git a/test/Cases/RouteTest.php b/test/Cases/RouteTest.php deleted file mode 100644 index d42ac088..00000000 --- a/test/Cases/RouteTest.php +++ /dev/null @@ -1,118 +0,0 @@ - - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -use Swoft\Http\Message\Testing\Web\Request; -use Swoft\Http\Message\Testing\Web\Response; - -class RouteTest extends AbstractTestCase -{ - /** - * @covers \App\Controllers\RouteController@funcArgs - */ - public function testFuncArgs() - { - $data = [ - 456, - 123, - true, - 'test', - Request::class, - Response::class, - ]; - $response = $this->request('GET', '/route/user/123/book/456/1/test', [], parent::ACCEPT_JSON); - $response->assertExactJson($data); - } - - /** - * @covers \App\Controllers\RouteController::hasNotArgs - */ - public function testHasNotArg() - { - $response = $this->request('GET', '/route/hasNotArg', [], parent::ACCEPT_JSON); - $response->assertExactJson(['data' => 'hasNotArg']); - } - - /** - * @covers \App\Controllers\RouteController@hasAnyArgs - */ - public function testHasAnyArgs() - { - $response = $this->request('GET', '/route/hasAnyArgs/123', [], parent::ACCEPT_JSON); - $response->assertExactJson([Request::class, 123]); - } - - /** - * @covers \App\Controllers\RouteController@optionalParameter - */ - public function testOptionnalParameter() - { - $response = $this->request('GET', '/route/opntion/arg1', [], parent::ACCEPT_JSON); - $response->assertExactJson(['arg1']); - - $response = $this->request('GET', '/route/opntion', [], parent::ACCEPT_JSON); - $response->assertExactJson(['']); - } - - /** - * @covers \App\Controllers\RouteController@hasMoreArgs - */ - public function testHasMoreArgs() - { - $response = $this->request('GET', '/route/hasMoreArgs', [], parent::ACCEPT_JSON); - $response->assertExactJson([Request::class, 0]); - } - - /** - * @covers \App\Controllers\RouteController@notAnnotation - */ - public function testNotAnnotation() - { - $response = $this->request('GET', '/route/notAnnotation', [], parent::ACCEPT_JSON); - $response->assertExactJson([Request::class]); - } - - /** - * @covers \App\Controllers\RouteController@onlyFunc - */ - public function testOnlyFunc() - { - $response = $this->request('GET', '/route/onlyFunc', [], parent::ACCEPT_JSON); - $response->assertExactJson([Request::class]); - } - - /** - * @covers \App\Controllers\RouteController@behind - */ - public function testBehindAction() - { - $response = $this->request('GET', '/route/behind', [], parent::ACCEPT_JSON); - $response->assertExactJson([Request::class]); - } - - /** - * @covers \App\Controllers\RouteController@funcAnyName - */ - public function testFuncAnyName() - { - $response = $this->request('GET', '/route/anyName/stelin', [], parent::ACCEPT_JSON); - $response->assertExactJson(['stelin']); - } -} \ No newline at end of file diff --git a/test/Cases/ValidatorControllerTest.php b/test/Cases/ValidatorControllerTest.php deleted file mode 100644 index 53f79c97..00000000 --- a/test/Cases/ValidatorControllerTest.php +++ /dev/null @@ -1,201 +0,0 @@ - - * @copyright Copyright 2010-2016 swoft software - * @license PHP Version 7.x {@link http://www.php.net/license/3_0.txt} - */ -class ValidatorControllerTest extends AbstractTestCase -{ - /** - * @covers \App\Controllers\ValidatorController::string - */ - public function testString() - { - $response = $this->request('GET', '/validator/string/swoft', [], parent::ACCEPT_JSON); - $response->assertExactJson(['boy', 'girl', 'swoft']); - - $response = $this->request('POST', '/validator/string/c', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name length is too short (minimum is 3)']); - - $response = $this->request('POST', '/validator/string/swoft', ['name' => 'a'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name length is too short (minimum is 3)']); - - $response = $this->request('POST', '/validator/string/swoft?name=b', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name length is too short (minimum is 3)']); - - $response = $this->request('POST', '/validator/string/swoft66666666', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name length is too long (maximum is 10)']); - - $response = $this->request('POST', '/validator/string/swoft', ['name' => 'swoft66666666'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name length is too long (maximum is 10)']); - - $response = $this->request('POST', '/validator/string/swoft?name=swoft66666666', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name length is too long (maximum is 10)']); - - $response = $this->request('POST', '/validator/string/swoftPath?name=swoftGet', ['name' => 'swoftPost'], parent::ACCEPT_JSON); - $response->assertExactJson(['swoftGet', 'swoftPost', 'swoftPath']); - - $response = $this->request('GET', '/validator/stringTpl', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'name-3-10 must']); - } - - /** - * @covers \App\Controllers\ValidatorController::number - */ - public function testNumber() - { - $response = $this->request('GET', '/validator/number/10', [], parent::ACCEPT_JSON); - $response->assertExactJson([7, 8, 10]); - - $response = $this->request('POST', '/validator/number/3', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/number/6', ['id' => '-2'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not a number']); - - $response = $this->request('POST', '/validator/number/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/number/6?id=-2', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not a number']); - - $response = $this->request('POST', '/validator/number/6?id=2', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/number/12', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); - - $response = $this->request('POST', '/validator/number/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); - - $response = $this->request('POST', '/validator/number/9?id=12', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); - - $response = $this->request('POST', '/validator/number/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); - $response->assertExactJson(['9', '9', 9]); - - $response = $this->request('GET', '/validator/numberTpl', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'id-5-10 must']); - } - - /** - * @covers \App\Controllers\ValidatorController::float - */ - public function testFloat() - { - $response = $this->request('GET', '/validator/float/a', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not float type']); - - $response = $this->request('GET', '/validator/float/5', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not float type']); - - $response = $this->request('POST', '/validator/float/5.0', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/float/6.0', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 5)']); - - $response = $this->request('POST', '/validator/float/5.2', ['id' => 5], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not float type']); - - $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.0'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/float/5.2', ['id' => '6.0'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 5)']); - - $response = $this->request('POST', '/validator/float/5.2', ['id' => '5.2'], parent::ACCEPT_JSON); - $response->assertExactJson([5.6, '5.2', 5.2]); - - $response = $this->request('POST', '/validator/float/5.2?id=5', [5.2], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not float type']); - - $response = $this->request('POST', '/validator/float/5.2?id=5.0', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/float/5.2?id=6.0', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 5)']); - - - $response = $this->request('POST', '/validator/float/5.2?id=5.2', ['id' => '5.2'], parent::ACCEPT_JSON); - $response->assertExactJson(['5.2', '5.2', 5.2]); - - $response = $this->request('GET', '/validator/floatTpl', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'id-5.1-5.9 must']); - } - - /** - * @covers \App\Controllers\ValidatorController::integer - */ - public function testInteger() - { - $response = $this->request('GET', '/validator/integer/10', [], parent::ACCEPT_JSON); - $response->assertExactJson([7, 8, 10]); - - $response = $this->request('POST', '/validator/integer/3', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/integer/6', ['id' => 'a'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not integer type']); - - $response = $this->request('POST', '/validator/integer/6', ['id' => '2'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/integer/6?id=a', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is not integer type']); - - $response = $this->request('POST', '/validator/integer/6?id=2', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too small (minimum is 5)']); - - $response = $this->request('POST', '/validator/integer/12', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); - - $response = $this->request('POST', '/validator/integer/9', ['id' => '12'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); - - $response = $this->request('POST', '/validator/integer/9?id=12', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter id is too big (maximum is 10)']); - - $response = $this->request('POST', '/validator/integer/9?id=9', ['id' => '9'], parent::ACCEPT_JSON); - $response->assertExactJson(['9', '9', 9]); - - $response = $this->request('GET', '/validator/integerTpl', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'id-5-10 must']); - } - - /** - * @covers \App\Controllers\ValidatorController::estring - */ - public function testEnum() - { - $response = $this->request('POST', '/validator/enum/4', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name is an invalid enum value']); - - $response = $this->request('POST', '/validator/enum/1?name=4', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name is an invalid enum value']); - - $response = $this->request('POST', '/validator/enum/1', ['name' => '4'], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'Parameter name is an invalid enum value']); - - $response = $this->request('POST', '/validator/enum/1?name=a', ['name' => '3'], parent::ACCEPT_JSON); - $response->assertExactJson(['a', '3', '1']); - - $response = $this->request('GET', '/validator/enumTpl', [], parent::ACCEPT_JSON); - $response->assertJsonFragment(['message' => 'name-null must']); - } -} \ No newline at end of file diff --git a/test/Dockerfile b/test/Dockerfile deleted file mode 100644 index f8a7ec93..00000000 --- a/test/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM swoft/swoft:latest - -MAINTAINER huangzhhui - -WORKDIR /var/www/swoft -RUN composer install \ - && composer dump-autoload -o \ - && composer clearcache diff --git a/test/README.md b/test/README.md deleted file mode 100644 index 3db8a55c..00000000 --- a/test/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Automated Testing - -## Unit test - -### Start -Docker Compose: `docker-compose up` - -## Standard of Script -1. All `TestCase` should be extends `\Swoft\Test\AbstractTestCase` class \ No newline at end of file diff --git a/test/bootstrap.php b/test/bootstrap.php deleted file mode 100644 index 1d450773..00000000 --- a/test/bootstrap.php +++ /dev/null @@ -1,26 +0,0 @@ - [ - 'class' => \Swoft\Testing\Application::class, - 'inTest' => true - ], -]); - -$initApplicationContext = new \Swoft\Core\InitApplicationContext(); -$initApplicationContext->init(); \ No newline at end of file diff --git a/test/docker-compose.yml b/test/docker-compose.yml deleted file mode 100644 index 114c6b66..00000000 --- a/test/docker-compose.yml +++ /dev/null @@ -1,9 +0,0 @@ -version: '2' -services: - swoft-unittest: - build: ./ - container_name: swoft-unittest - volumes: - - ../:/var/www/swoft - working_dir: /var/www/swoft/test - command: ../vendor/bin/phpunit --bootstrap ./bootstrap.php --verbose ./ \ No newline at end of file From aa51e2772b74bb7ae871c5efa5f8d86ccf2c2b87 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 12 Apr 2019 18:04:29 +0800 Subject: [PATCH 315/643] rm file --- .env.docker-compose | 8 ---- .env.example | 114 -------------------------------------------- changelog.md | 4 -- 3 files changed, 126 deletions(-) delete mode 100644 .env.docker-compose delete mode 100644 .env.example delete mode 100644 changelog.md diff --git a/.env.docker-compose b/.env.docker-compose deleted file mode 100644 index ce342032..00000000 --- a/.env.docker-compose +++ /dev/null @@ -1,8 +0,0 @@ -# Database Master nodes -DB_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 - -# Database Slave nodes -DB_SLAVE_URI=mysql:3306/test?user=root&password=123456&charset=utf8,mysql:3306/test?user=root&password=123456&charset=utf8 - -# Redis -REDIS_URI=redis:6379,redis:6379 \ No newline at end of file diff --git a/.env.example b/.env.example deleted file mode 100644 index 74c94abe..00000000 --- a/.env.example +++ /dev/null @@ -1,114 +0,0 @@ -# Application -TIME_ZONE=Asia/Shanghai -LOG_ENABLE=false -APP_DEBUG=false - -# Server -PFILE=@runtime/swoft.pid -PNAME=php-swoft -TCPABLE=true -CRONABLE=false -AUTO_RELOAD=true -AUTO_REGISTER=false - -# HTTP -HTTP_HOST=0.0.0.0 -HTTP_PORT=80 -HTTP_MODE=SWOOLE_PROCESS -HTTP_TYPE=SWOOLE_SOCK_TCP - -# WebSocket -WS_ENABLE_HTTP=true - -# TCP -TCP_HOST=0.0.0.0 -TCP_PORT=8099 -TCP_MODE=SWOOLE_PROCESS -TCP_TYPE=SWOOLE_SOCK_TCP -TCP_PACKAGE_MAX_LENGTH=2048 -TCP_OPEN_EOF_CHECK=false - -# Crontab -CRONTAB_TASK_COUNT=1024 -CRONTAB_TASK_QUEUE=2048 - -# Swoole Settings -WORKER_NUM=1 -MAX_REQUEST=100000 -DAEMONIZE=0 -DISPATCH_MODE=2 -TASK_IPC_MODE=1 -MESSAGE_QUEUE_KEY=1879052289 -TASK_TMPDIR=/tmp/ -LOG_FILE=@runtime/logs/swoole.log -TASK_WORKER_NUM=1 -PACKAGE_MAX_LENGTH=2048 -OPEN_HTTP2_PROTOCOL=false -SSL_CERT_FILE=/path/to/ssl_cert_file -SSL_KEY_FILE=/path/to/ssl_key_file - -# Database Master nodes -DB_NAME=dbMaster -DB_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8 -DB_MIN_ACTIVE=5 -DB_MAX_ACTIVE=10 -DB_MAX_WAIT=20 -DB_MAX_WAIT_TIME=3 -DB_MAX_IDLE_TIME=60 -DB_TIMEOUT=2 - -# Database Slave nodes -DB_SLAVE_NAME=dbSlave -DB_SLAVE_URI=127.0.0.1:3306/test?user=root&password=123456&charset=utf8,127.0.0.1:3306/test?user=root&password=123456&charset=utf8 -DB_SLAVE_MIN_ACTIVE=5 -DB_SLAVE_MAX_ACTIVE=10 -DB_SLAVE_MAX_WAIT=20 -DB_SLAVE_MAX_WAIT_TIME=3 -DB_SLAVE_MAX_IDLE_TIME=60 -DB_SLAVE_TIMEOUT=3 - -# Redis -REDIS_NAME=redis -REDIS_DB=2 -REDIS_URI=127.0.0.1:6379,127.0.0.1:6379 -REDIS_MIN_ACTIVE=5 -REDIS_MAX_ACTIVE=10 -REDIS_MAX_WAIT=20 -REDIS_MAX_WAIT_TIME=3 -REDIS_MAX_IDLE_TIME=60 -REDIS_TIMEOUT=3 -REDIS_SERIALIZE=1 - -# other redis node -REDIS_DEMO_REDIS_DB=6 -REDIS_DEMO_REDIS_PREFIX=demo_redis_ - -# User service (demo service) -USER_POOL_NAME=user -USER_POOL_URI=127.0.0.1:8099,127.0.0.1:8099 -USER_POOL_MIN_ACTIVE=5 -USER_POOL_MAX_ACTIVE=10 -USER_POOL_MAX_WAIT=20 -USER_POOL_TIMEOUT=200 -USER_POOL_MAX_WAIT_TIME=3 -USER_POOL_MAX_IDLE_TIME=60 -USER_POOL_USE_PROVIDER=false -USER_POOL_BALANCER=random -USER_POOL_PROVIDER=consul - -# User service breaker (demo service) -USER_BREAKER_FAIL_COUNT = 3 -USER_BREAKER_SUCCESS_COUNT = 6 -USER_BREAKER_DELAY_TIME = 5000 - -# Consul -CONSUL_ADDRESS=http://127.0.0.1 -CONSUL_PORT=8500 -CONSUL_REGISTER_NAME=user -CONSUL_REGISTER_ETO=false -CONSUL_REGISTER_SERVICE_ADDRESS=127.0.0.1 -CONSUL_REGISTER_SERVICE_PORT=8099 -CONSUL_REGISTER_CHECK_NAME=user -CONSUL_REGISTER_CHECK_TCP=127.0.0.1:8099 -CONSUL_REGISTER_CHECK_INTERVAL=10 -CONSUL_REGISTER_CHECK_TIMEOUT=1 diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 91829aca..00000000 --- a/changelog.md +++ /dev/null @@ -1,4 +0,0 @@ -# change log - -1. 新增对象池 -2. prototype优化,clone \ No newline at end of file From 45e1e7197c5e251e70af4303446ccd982fb60076 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 12 Apr 2019 18:06:01 +0800 Subject: [PATCH 316/643] rm php_cs --- .php_cs | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .php_cs diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 41e1b734..00000000 --- a/.php_cs +++ /dev/null @@ -1,38 +0,0 @@ -setRiskyAllowed(true) - ->setRules([ - '@PSR2' => true, - 'header_comment' => [ - 'commentType' => 'PHPDoc', - 'header' => $header, - 'separate' => 'none' - ], - 'array_syntax' => [ - 'syntax' => 'short' - ], - 'single_quote' => true, - 'class_attributes_separation' => true, - 'no_unused_imports' => true, - 'standardize_not_equals' => true, - ]) - ->setFinder( - PhpCsFixer\Finder::create() - ->exclude('public') - ->exclude('resources') - ->exclude('config') - ->exclude('runtime') - ->exclude('vendor') - ->in(__DIR__) - ) - ->setUsingCache(false); From e001d7519bb346df97f03b958fd03ba660c34e8e Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 13 Apr 2019 10:19:10 +0800 Subject: [PATCH 317/643] Modify composer --- .travis.yml | 18 ------------------ composer.json | 2 +- run | 12 ------------ 3 files changed, 1 insertion(+), 31 deletions(-) delete mode 100644 .travis.yml delete mode 100644 run diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ad955eb1..00000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: php - -php: - - 7.0 - - 7.1 - - 7.2 - -install: - - wget https://github.com/redis/hiredis/archive/v0.13.3.tar.gz -O hiredis.tar.gz && mkdir -p hiredis && tar -xf hiredis.tar.gz -C hiredis --strip-components=1 && cd hiredis && sudo make -j$(nproc) && sudo make install && sudo ldconfig && cd .. - - printf "\n" | pecl install -f swoole-2.1.3 -before_script: - - composer update --dev - -script: composer test - -cache: - directories: - - "$HOME/.composer/cache/files" \ No newline at end of file diff --git a/composer.json b/composer.json index edf24c9d..65e8c7de 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "swoft/task": "^2.0", "swoft/redis": "^2.0", "swoft/proxy": "^2.0", - "swoft/component": "2.0.x-dev as 2.0" + "swoft/component": "dev-master as 2.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", diff --git a/run b/run deleted file mode 100644 index 8afe95ce..00000000 --- a/run +++ /dev/null @@ -1,12 +0,0 @@ -GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.el7 -Copyright (C) 2013 Free Software Foundation, Inc. -License GPLv3+: GNU GPL version 3 or later -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. Type "show copying" -and "show warranty" for details. -This GDB was configured as "x86_64-redhat-linux-gnu". -For bug reporting instructions, please see: -... -Reading symbols from /usr/local/php/bin/php...done. -[?1034h(gdb) ^C(gdb) ^C(gdb) q quti -(gdb) ^C(gdb) ^C(gdb) ^C(gdb) ^C(gdb) ^C(gdb) ^Z \ No newline at end of file From 7cb17d5359973f505794f0ce2e6a57c23d86e286 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 13 Apr 2019 11:08:22 +0800 Subject: [PATCH 318/643] M composer --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 65e8c7de..8e66dd3b 100644 --- a/composer.json +++ b/composer.json @@ -61,7 +61,7 @@ "repositories": [ { "type": "git", - "url": "https://github.com/swoft-cloud/swoft-component.git" + "url": "git@github.com:swoft-cloud/swoft-component.git" }, { "type": "composer", From bc9b6ee7b4e1714a2b90686cf608ffd8e20b85f2 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 13 Apr 2019 11:09:48 +0800 Subject: [PATCH 319/643] remove dep: psy/psysh (#592) --- composer.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 8e66dd3b..91ef62a5 100644 --- a/composer.json +++ b/composer.json @@ -41,8 +41,7 @@ "require-dev": { "swoft/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^7.5", - "friendsofphp/php-cs-fixer": "^2.10", - "psy/psysh": "@stable" + "friendsofphp/php-cs-fixer": "^2.10" }, "autoload": { "psr-4": { @@ -68,4 +67,4 @@ "url": "https://packagist.laravel-china.org/" } ] -} \ No newline at end of file +} From 3ddf2dc5187e6a6f1f4673ff29944ed642b4cbc5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 13 Apr 2019 11:11:11 +0800 Subject: [PATCH 320/643] Rm metda --- .phpstorm.meta.php | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .phpstorm.meta.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php deleted file mode 100644 index fff23cc3..00000000 --- a/.phpstorm.meta.php +++ /dev/null @@ -1,20 +0,0 @@ - \Swoft\Defer\Defer::class, - ]; - override(\bean(0), map($map)); - override(\Swoft\App::getBean(0), map($map)); - override(\Swoft\Core\ApplicationContext::getBean(0), map($map)); - override(\Swoft\Bean\BeanFactory::getBean(0), map($map)); - -} \ No newline at end of file From 9555dcfa14a98a5d9d1d85ed96312a14e40a6b08 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 13 Apr 2019 14:16:37 +0800 Subject: [PATCH 321/643] Refactor dir --- app/Application.php | 7 +- app/Aspect/AnnotationAspect.php | 14 + app/Aspect/LogAspect.php | 75 - app/Aspect/LogAspect2.php | 76 - app/AutoLoader.php | 20 +- app/{Rpc/Middleware => Console/Command}/.keep | 0 app/Controller/TestController.php | 151 - app/Http/Controller/RedisController.php | 35 + app/Http/Middleware/ControllerMiddleware.php | 10 + app/Model/Dao/DemoDao.php | 17 - app/Model/Dao/UserDao.php | 10 + app/Model/Data/DemoData.php | 25 - app/Model/Data/UserData.php | 10 + app/Model/Entity/User.php | 2 +- app/Model/Logic/DemoLogic.php | 89 - app/Model/Logic/UserLogic.php | 10 + app/Rpc/Controller/RpcController.php | 76 - app/Rpc/Lib/UserInterface.php | 32 +- app/Rpc/Middleware/ServiceMiddleware.php | 23 + app/Rpc/Service/UserService.php | 51 - app/Rpc/Service/UserService2.php | 69 - app/Rpc/Service/UserServiceV2.php | 17 + app/Task/Controller/TaskController.php | 33 - app/Task/Listener/FinishListener.php | 14 + app/Task/Task/AsyncTask.php | 10 + app/Task/Task/CoTask.php | 10 + app/Task/TestTask.php | 25 - app/WebSocket/Chat/ChatController.php | 31 - app/WebSocket/ChatModule.php | 33 +- app/WebSocket/Middleware/.keep | 0 app/bean.php | 9 - bin/phpunit | 69122 ---------------- 32 files changed, 192 insertions(+), 69914 deletions(-) create mode 100644 app/Aspect/AnnotationAspect.php delete mode 100644 app/Aspect/LogAspect.php delete mode 100644 app/Aspect/LogAspect2.php rename app/{Rpc/Middleware => Console/Command}/.keep (100%) delete mode 100644 app/Controller/TestController.php create mode 100644 app/Http/Controller/RedisController.php create mode 100644 app/Http/Middleware/ControllerMiddleware.php delete mode 100644 app/Model/Dao/DemoDao.php create mode 100644 app/Model/Dao/UserDao.php delete mode 100644 app/Model/Data/DemoData.php create mode 100644 app/Model/Data/UserData.php delete mode 100644 app/Model/Logic/DemoLogic.php create mode 100644 app/Model/Logic/UserLogic.php delete mode 100644 app/Rpc/Controller/RpcController.php create mode 100644 app/Rpc/Middleware/ServiceMiddleware.php delete mode 100644 app/Rpc/Service/UserService2.php create mode 100644 app/Rpc/Service/UserServiceV2.php delete mode 100644 app/Task/Controller/TaskController.php create mode 100644 app/Task/Listener/FinishListener.php create mode 100644 app/Task/Task/AsyncTask.php create mode 100644 app/Task/Task/CoTask.php delete mode 100644 app/Task/TestTask.php delete mode 100644 app/WebSocket/Chat/ChatController.php delete mode 100644 app/WebSocket/Middleware/.keep delete mode 100755 bin/phpunit diff --git a/app/Application.php b/app/Application.php index 90779854..2ec069e3 100644 --- a/app/Application.php +++ b/app/Application.php @@ -1,4 +1,5 @@ -getReturn(); - echo 'apsect1 afterReturn ' . PHP_EOL; - - return 'new value afterReturn '; - } - - /** - * @Around() - * @param ProceedingJoinPoint $proceedingJoinPoint - * - * @return mixed - */ - public function around(ProceedingJoinPoint $proceedingJoinPoint) - { - echo 'apsect1 around before ' . PHP_EOL; - $result = $proceedingJoinPoint->proceed(); - echo 'apsect1 around after ' . PHP_EOL; - return $result; - } - - /** - * @AfterThrowing() - */ - public function afterThrowing() - { - echo "apsect1 afterThrowing !\n"; - } -} \ No newline at end of file diff --git a/app/Aspect/LogAspect2.php b/app/Aspect/LogAspect2.php deleted file mode 100644 index 7877f722..00000000 --- a/app/Aspect/LogAspect2.php +++ /dev/null @@ -1,76 +0,0 @@ -getReturn(); - echo 'apsect2 afterReturn ' . PHP_EOL; - - return 'new value afterReturn2 '; - } - - /** - * @Around() - * @param ProceedingJoinPoint $proceedingJoinPoint - * - * @return mixed - */ - public function around(ProceedingJoinPoint $proceedingJoinPoint) - { - echo 'apsect2 around before ' . PHP_EOL; - $result = $proceedingJoinPoint->proceed(); - echo 'apsect2 around after ' . PHP_EOL; - return $result; - } - - /** - * @AfterThrowing() - */ - public function afterThrowing() - { - echo "apsect2 afterThrowing !\n"; - } -} \ No newline at end of file diff --git a/app/AutoLoader.php b/app/AutoLoader.php index 87cc5480..8e72de28 100644 --- a/app/AutoLoader.php +++ b/app/AutoLoader.php @@ -1,17 +1,19 @@ - __DIR__, ]; } + + /** + * @return array + */ + public function metadata(): array + { + return []; + } } \ No newline at end of file diff --git a/app/Rpc/Middleware/.keep b/app/Console/Command/.keep similarity index 100% rename from app/Rpc/Middleware/.keep rename to app/Console/Command/.keep diff --git a/app/Controller/TestController.php b/app/Controller/TestController.php deleted file mode 100644 index 3238e324..00000000 --- a/app/Controller/TestController.php +++ /dev/null @@ -1,151 +0,0 @@ -toArray()); - } - - /** - * @RequestMapping(route="ts") - * - * @return false|string - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException - * @throws \Swoft\Db\Exception\PoolException - */ - public function ts() - { - DB::pool()->beginTransaction(); - $user = User::find(22); - - \sgo(function () { - DB::pool()->beginTransaction(); - $user = User::find(22); - }); - - return json_encode($user->toArray()); - } - - /** - * @RequestMapping(route="cm") - * - * @return false|string - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException - * @throws \Swoft\Db\Exception\PoolException - */ - public function cm() - { - DB::pool()->beginTransaction(); - $user = User::find(22); - DB::pool()->commit(); - - \sgo(function () { - DB::pool()->beginTransaction(); - $user = User::find(22); - DB::pool()->commit(); - }); - - return json_encode($user->toArray()); - } - - /** - * @RequestMapping(route="rl") - * - * @return false|string - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException - * @throws \Swoft\Db\Exception\PoolException - */ - public function rl() - { - DB::pool()->beginTransaction(); - $user = User::find(22); - DB::pool()->rollBack(); - - \sgo(function () { - DB::pool()->beginTransaction(); - $user = User::find(22); - DB::pool()->rollBack(); - }); - - return json_encode($user->toArray()); - } - - - /** - * @param Response $response - * @param Request $request - * - * @RequestMapping(route="p") - * - * @return string - */ - public function params(Response $response, Request $request): string - { - return get_class($response) . '-' . get_class($request); - } - - /** - * @param int $uid - * @param Response $response - * @param string $name - * @param int $age - * @param int $count - * - * @RequestMapping(route="u/{uid}") - * - * @return string - */ - public function user(int $uid, Response $response, string $name, int $age, int $count = 1): string - { - return 'uid=' . $uid . ' name=' . $name . ' age=' . $age . ' count=' . $count . ' response=' . get_class($response); - } - - /** - * @RequestMapping(route="redis") - * - * @return array - */ - public function redis(): array - { - Redis::set('a', 'b'); - $a = Redis::get('a'); - - return [$a]; - } -} \ No newline at end of file diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php new file mode 100644 index 00000000..38be5527 --- /dev/null +++ b/app/Http/Controller/RedisController.php @@ -0,0 +1,35 @@ +dao->get(); - } -} \ No newline at end of file diff --git a/app/Model/Data/UserData.php b/app/Model/Data/UserData.php new file mode 100644 index 00000000..a2db6502 --- /dev/null +++ b/app/Model/Data/UserData.php @@ -0,0 +1,10 @@ +name = $name; - $this->type = $type; - - $this->construnctData = $data; - } - - /** - * @return string - */ - public function getData(): DemoData - { - $this->data->getDao(); - echo 'name=' . $this->name . PHP_EOL; - echo 'type=' . $this->type . PHP_EOL; - echo 'configName=' . $this->configName . PHP_EOL; - echo 'configHost=' . $this->configHost . PHP_EOL; - - return $this->construnctData; - } - - /** - * @return string - */ - public function getDefinitionData(): string - { - return $this->definitionData; - } -} \ No newline at end of file diff --git a/app/Model/Logic/UserLogic.php b/app/Model/Logic/UserLogic.php new file mode 100644 index 00000000..0397d929 --- /dev/null +++ b/app/Model/Logic/UserLogic.php @@ -0,0 +1,10 @@ +userService->getUsers([12, 16]); - $user = $this->userService->getUser(12); - $user2 = $this->userService->getByName('name'); - $bool = $this->userService->isExist(12); - - $data = [ - $users, - $user, - $user2, - $bool, - ]; - return $data; - } - - /** - * @RequestMapping("user2") - * - * @return array - */ - public function user2(): array - { - $users = $this->userService2->getUsers([12, 16]); - $user = $this->userService2->getUser(12); - $user2 = $this->userService2->getByName('name'); - $bool = $this->userService2->isExist(12); - - $data = [ - $users, - $user, - $user2, - $bool, - ]; - return $data; - } -} \ No newline at end of file diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index 8869f45e..667d55b9 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -3,34 +3,12 @@ namespace App\Rpc\Lib; - +/** + * Class UserInterface + * + * @since 2.0 + */ interface UserInterface { - /** - * @param int $id - * - * @return array - */ - public function getUser(int $id): array; - - /** - * @param int $id - * - * @return bool - */ - public function isExist(int $id): bool; - - /** - * @param string $name - * - * @return int - */ - public function getByName(string $name): int; - /** - * @param array $ids - * - * @return array - */ - public function getUsers(array $ids): array; } \ No newline at end of file diff --git a/app/Rpc/Middleware/ServiceMiddleware.php b/app/Rpc/Middleware/ServiceMiddleware.php new file mode 100644 index 00000000..33d157e9 --- /dev/null +++ b/app/Rpc/Middleware/ServiceMiddleware.php @@ -0,0 +1,23 @@ +handle($request); + } +} \ No newline at end of file diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index a1abf9a4..4010de1d 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -5,64 +5,13 @@ use App\Rpc\Lib\UserInterface; -use Swoft\Rpc\Server\Annotation\Mapping\Service; /** * Class UserService * * @since 2.0 - * - * @Service() */ class UserService implements UserInterface { - /** - * @param int $id - * - * @return array - */ - public function getUser(int $id): array - { - return [ - 'id' => $id, - 'name' => 'name' - ]; - } - - /** - * @param int $id - * - * @return bool - */ - public function isExist(int $id): bool - { - return $id == 1; - } - - /** - * @param string $name - * - * @return int - */ - public function getByName(string $name): int - { - return 18306; - } - - /** - * @param array $ids - * - * @return array - */ - public function getUsers(array $ids): array - { - $users = []; - foreach ($ids as $id) { - $user['id'] = $id; - $user['name'] = 'name' . $id; - $users[] = $user; - } - return $users; - } } \ No newline at end of file diff --git a/app/Rpc/Service/UserService2.php b/app/Rpc/Service/UserService2.php deleted file mode 100644 index 408e9b03..00000000 --- a/app/Rpc/Service/UserService2.php +++ /dev/null @@ -1,69 +0,0 @@ - '1.1', - 'id' => $id, - 'name' => 'name' - ]; - } - - /** - * @param int $id - * - * @return bool - */ - public function isExist(int $id): bool - { - return $id == 1; - } - - /** - * @param string $name - * - * @return int - */ - public function getByName(string $name): int - { - return 18306 + 10000; - } - - /** - * @param array $ids - * - * @return array - */ - public function getUsers(array $ids): array - { - $users = []; - foreach ($ids as $id) { - $users['id'] = $id; - $users['v'] = '1.1'; - $users['name'] = 'name' . $id; - } - - return $users; - } -} \ No newline at end of file diff --git a/app/Rpc/Service/UserServiceV2.php b/app/Rpc/Service/UserServiceV2.php new file mode 100644 index 00000000..f17b07f8 --- /dev/null +++ b/app/Rpc/Service/UserServiceV2.php @@ -0,0 +1,17 @@ + [ - [ - 'dDname', - 12, - '${App\Model\Data\DemoData}' - ], - 'definitionData' => 'definitionData...' - ], - 'logger' => [ 'flushRequest' => false, 'enable' => false, diff --git a/bin/phpunit b/bin/phpunit deleted file mode 100755 index 5e0bba07..00000000 --- a/bin/phpunit +++ /dev/null @@ -1,69122 +0,0 @@ -#!/usr/bin/env php -')) { - fwrite( - STDERR, - sprintf( - 'PHPUnit 7.5.6 by Sebastian Bergmann and contributors.' . PHP_EOL . PHP_EOL . - 'This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . - 'You are using PHP %s (%s).' . PHP_EOL, - PHP_VERSION, - PHP_BINARY - ) - ); - - die(1); -} - -if (__FILE__ === realpath($_SERVER['SCRIPT_NAME'])) { - $execute = true; -} else { - $execute = false; -} - -$options = getopt('', array('prepend:', 'manifest')); - -if (isset($options['prepend'])) { - require $options['prepend']; -} - -if (isset($options['manifest'])) { - $printManifest = true; -} - -unset($options); - -define('__PHPUNIT_PHAR__', str_replace(DIRECTORY_SEPARATOR, '/', __FILE__)); -define('__PHPUNIT_PHAR_ROOT__', 'phar://phpunit-7.5.6.phar'); - -Phar::mapPhar('phpunit-7.5.6.phar'); - -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/DeepCopy.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Exception/CloneException.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Exception/PropertyException.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Filter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Matcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php'; -require 'phar://phpunit-7.5.6.phar' . '/myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php'; -require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Assert.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SelfDescribing.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/AssertionFailedError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/CodeCoverageException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Constraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ArrayHasKey.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ArraySubset.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Composite.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Attribute.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Callback.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ClassHasAttribute.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ClassHasStaticAttribute.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Count.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/DirectoryExists.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ExceptionCode.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ExceptionMessage.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ExceptionMessageRegularExpression.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/FileExists.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/GreaterThan.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsAnything.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsEmpty.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsEqual.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsFalse.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsFinite.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsIdentical.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsInfinite.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsInstanceOf.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsJson.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsNan.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsNull.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsReadable.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsTrue.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsType.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/IsWritable.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/JsonMatches.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LessThan.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalAnd.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalNot.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalOr.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/LogicalXor.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/ObjectHasAttribute.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/RegularExpression.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/SameSize.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringContains.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringEndsWith.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringMatchesFormatDescription.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/StringStartsWith.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/TraversableContains.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Constraint/TraversableContainsOnly.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/RiskyTest.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/RiskyTestError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/CoveredCodeNotExecutedException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Test.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestSuite.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/DataProviderTestSuite.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Error.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Deprecated.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Notice.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Error/Warning.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/ExceptionWrapper.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/ExpectationFailedException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/IncompleteTest.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestCase.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/IncompleteTestCase.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/IncompleteTestError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/InvalidCoversTargetException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MissingCoversAnnotationException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Exception/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Exception/BadMethodCallException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/Identity.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/Stub.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/Match.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/ParametersMatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/MethodNameMatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/InvocationMocker.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Builder/NamespaceMatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Generator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invocation/Invocation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/MatcherCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Verifiable.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invokable.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/InvocationMocker.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invocation/StaticInvocation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Invocation/ObjectInvocation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/Invocation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedRecorder.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/AnyInvokedCount.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/StatelessInvocation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/AnyParameters.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/ConsecutiveParameters.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/DeferredError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtIndex.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtLeastCount.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtLeastOnce.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedAtMostCount.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/InvokedCount.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/MethodName.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Matcher/Parameters.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockBuilder.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockMethod.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockMethodSet.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/MockObject.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/ForwardCompatibility/MockObject.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Exception/RuntimeException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnArgument.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnCallback.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnReference.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnSelf.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnStub.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/MockObject/Stub/ReturnValueMap.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/OutputError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTest.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTestCase.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTestError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SkippedTestSuiteError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/SyntheticError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestFailure.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestListener.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestListenerDefaultImplementation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestResult.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/TestSuiteIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/UnintentionallyCoveredCodeError.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/Warning.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Framework/WarningTestCase.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/Hook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/TestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterIncompleteTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterLastTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterRiskyTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterSkippedTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterSuccessfulTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestErrorHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestFailureHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/AfterTestWarningHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/BaseTestRunner.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/BeforeFirstTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/BeforeTestHook.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/GroupFilterIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/ExcludeGroupFilterIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/Factory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/IncludeGroupFilterIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Filter/NameFilterIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestResultCacheInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/NullTestResultCache.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/PhptTestCase.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/ResultCacheExtension.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/TestSuiteLoader.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/StandardTestSuiteLoader.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Hook/TestListenerAdapter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestResultCache.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/TestSuiteSorter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Runner/Version.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/TextUI/Command.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Printer.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/TextUI/ResultPrinter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/TextUI/TestRunner.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Blacklist.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Configuration.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/ConfigurationGenerator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/ErrorHandler.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/FileLoader.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Filesystem.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Filter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Getopt.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/GlobalState.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/InvalidArgumentHelper.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Json.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Log/JUnit.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Log/TeamCity.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/PHP/AbstractPhpProcess.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/PHP/DefaultPhpProcess.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/PHP/WindowsPhpProcess.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/RegularExpression.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Test.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/CliTestDoxPrinter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/ResultPrinter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/HtmlResultPrinter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/NamePrettifier.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/TestResult.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/TextResultPrinter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TestDox/XmlResultPrinter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/TextTestListRenderer.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Type.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/XdebugFilterScriptGenerator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/Xml.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpunit/Util/XmlTestListRenderer.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-token-stream/Token.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-token-stream/Token/Stream.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-token-stream/Token/Stream/CachingFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Type.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Application.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/ApplicationName.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Author.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/AuthorCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/AuthorCollectionIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ManifestElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/AuthorElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ElementCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/AuthorElementCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/BundledComponent.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/BundledComponentCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/BundledComponentCollectionIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/BundlesElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ComponentElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ComponentElementCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ContainsElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/CopyrightElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/CopyrightInformation.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Email.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ExtElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ExtElementCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Extension.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ExtensionElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/InvalidApplicationNameException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/InvalidEmailException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/InvalidUrlException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Library.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/License.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/LicenseElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Manifest.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ManifestDocument.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestDocumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/ManifestDocumentLoadingException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/ManifestDocumentMapper.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestDocumentMapperException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestElementException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/ManifestLoader.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/exceptions/ManifestLoaderException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/ManifestSerializer.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/PhpElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Requirement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/PhpExtensionRequirement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/PhpVersionRequirement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/RequirementCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/RequirementCollectionIterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/xml/RequiresElement.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-manifest/values/Url.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/VersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/AbstractVersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/AndVersionConstraintGroup.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/AnyVersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/ExactVersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/InvalidPreReleaseSuffixException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/InvalidVersionException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/OrVersionConstraintGroup.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/PreReleaseSuffix.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/constraints/SpecificMajorVersionConstraint.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/exceptions/UnsupportedVersionConstraintException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/Version.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/VersionConstraintParser.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/VersionConstraintValue.php'; -require 'phar://phpunit-7.5.6.phar' . '/phar-io-version/VersionNumber.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Call/Call.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Call/CallCenter.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/Comparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/Factory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Comparator/Factory.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ArrayComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ObjectComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Doubler.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/LazyDouble.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Doubler/NameGenerator.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallPrediction.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/PromiseInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/CallbackPromise.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/ReturnPromise.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophecy/Revealer.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Prophet.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Util/ExportUtil.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpspec-prophecy/Prophecy/Util/StringUtil.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/CodeCoverage.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/RuntimeException.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/CoveredCodeNotExecutedException.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Driver/Driver.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Driver/PHPDBG.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Driver/Xdebug.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Filter.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/MissingCoversAnnotationException.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/AbstractNode.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/Builder.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/Directory.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/File.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Node/Iterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Clover.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Crap4j.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer/Dashboard.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer/Directory.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Facade.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Html/Renderer/File.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/PHP.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Text.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/BuildInformation.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Coverage.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Node.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Directory.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Facade.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/File.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Method.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Project.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Report.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Source.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Tests.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Totals.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Report/Xml/Unit.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Exception/UnintentionallyCoveredCodeException.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Util.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-code-coverage/Version.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-code-unit-reverse-lookup/Wizard.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ComparisonFailure.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/DOMNodeComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/DateTimeComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ScalarComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/NumericComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/DoubleComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ExceptionComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/MockObjectComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/ResourceComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/SplObjectStorageComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-comparator/TypeComparator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Chunk.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Exception/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Exception/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Exception/ConfigurationException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Diff.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Differ.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Line.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/LongestCommonSubsequenceCalculator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/DiffOutputBuilderInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/AbstractChunkOutputBuilder.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/DiffOnlyOutputBuilder.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Output/UnifiedDiffOutputBuilder.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/Parser.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-environment/Console.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-environment/OperatingSystem.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-environment/Runtime.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-exporter/Exporter.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-file-iterator/Facade.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-file-iterator/Factory.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-file-iterator/Iterator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/Blacklist.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/CodeExporter.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/exceptions/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/Restorer.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/exceptions/RuntimeException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-global-state/Snapshot.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-invoker/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-invoker/Invoker.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-invoker/TimeoutException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-enumerator/Enumerator.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-enumerator/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-enumerator/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-reflector/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-reflector/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-object-reflector/ObjectReflector.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-recursion-context/Context.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-recursion-context/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-recursion-context/InvalidArgumentException.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-resource-operations/ResourceOperations.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-timer/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-timer/RuntimeException.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-timer/Timer.php'; -require 'phar://phpunit-7.5.6.phar' . '/sebastian-version/Version.php'; -require 'phar://phpunit-7.5.6.phar' . '/php-text-template/Template.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/Exception.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/NamespaceUri.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/NamespaceUriException.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/Token.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/TokenCollection.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/TokenCollectionException.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/Tokenizer.php'; -require 'phar://phpunit-7.5.6.phar' . '/theseer-tokenizer/XMLSerializer.php'; -require 'phar://phpunit-7.5.6.phar' . '/webmozart-assert/Assert.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlockFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Description.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Serializer.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/TagFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tag.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Example.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Link.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/See.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Source.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Element.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/File.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Fqsen.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/FqsenResolver.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Location.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/Project.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-reflection-common/ProjectFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Type.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/TypeResolver.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Array_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Boolean.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Callable_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Compound.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Context.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/ContextFactory.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Float_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Integer.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Iterable_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Mixed_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Null_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Nullable.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Object_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Parent_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Resource_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Scalar.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Self_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Static_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/String_.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/This.php'; -require 'phar://phpunit-7.5.6.phar' . '/phpdocumentor-type-resolver/Types/Void_.php'; - -if ($execute) { - if (isset($printManifest)) { - print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/manifest.txt'); - - exit; - } - - unset($execute); - - PHPUnit\TextUI\Command::main(); -} - -__HALT_COMPILER(); ?> -|�vphpunit-7.5.6.phar-phpdocumentor-type-resolver/FqsenResolver.php �yj\ �R�ٶ$phpdocumentor-type-resolver/Type.php��yj\���[L�/phpdocumentor-type-resolver/Types/Resource_.php��yj\�Nf ��-phpdocumentor-type-resolver/Types/Context.phpV �yj\V �{��,phpdocumentor-type-resolver/Types/Scalar.php�yj\U����-phpdocumentor-type-resolver/Types/Boolean.php��yj\��f��-phpdocumentor-type-resolver/Types/String_.php��yj\����-phpdocumentor-type-resolver/Types/Parent_.php4�yj\4��j�+phpdocumentor-type-resolver/Types/Self_.php�yj\��9'�.phpdocumentor-type-resolver/Types/Nullable.php��yj\��Ab��.phpdocumentor-type-resolver/Types/Compound.php �yj\ ���|�/phpdocumentor-type-resolver/Types/Iterable_.php��yj\�O��Ӷ+phpdocumentor-type-resolver/Types/Void_.phpW�yj\W��ֶ/phpdocumentor-type-resolver/Types/Callable_.php��yj\�4ɿ�,phpdocumentor-type-resolver/Types/Mixed_.php��yj\�ϯ�ʶ-phpdocumentor-type-resolver/Types/Object_.php��yj\���Q��,phpdocumentor-type-resolver/Types/Float_.php��yj\��w,�-phpdocumentor-type-resolver/Types/Integer.php��yj\�"s��*phpdocumentor-type-resolver/Types/This.php��yj\��h²�,phpdocumentor-type-resolver/Types/Array_.php-�yj\-��_�-phpdocumentor-type-resolver/Types/Static_.phpU�yj\U�ޟ �4phpdocumentor-type-resolver/Types/ContextFactory.php��yj\�E�3�+phpdocumentor-type-resolver/Types/Null_.php��yj\�@�%��,phpdocumentor-type-resolver/TypeResolver.phpw$�yj\w$U��y�#phpdocumentor-type-resolver/LICENSE8�yj\8��ʶobject-reflector/LICENSE -�yj\ -�F���Hphpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.php�yj\�:�F�Bphpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.php��yj\���$϶Hphpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php)�yj\)F��4�Fphpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.php��yj\�h+Ephpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.php��yj\�77/%�Jphpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.php��yj\������Jphpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.php��yj\����?phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.php��yj\�z�F��Dphpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.php��yj\�ۉ?�Gphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.php��yj\��X�@phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.php��yj\��Z^�Lphpspec-prophecy/Prophecy/Exception/Doubler/MethodNotExtendableException.phpD�yj\D�p��1phpspec-prophecy/Prophecy/Exception/Exception.php+�yj\+����@phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.php��yj\��g��Ephpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.php�yj\;��Pphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.php�yj\� ƶFphpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.php��yj\�2T�ѶKphpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php,�yj\,���a�Cphpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.php��yj\��l<��Lphpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpJ�yj\J~��D�Dphpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.php��yj\����5phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.php��yj\��5D�5phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.phpq1�yj\q1�?�8phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpH�yj\H�gZ��8phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php,�yj\,�W�/phpspec-prophecy/Prophecy/Prophecy/Revealer.php��yj\�j�ɸ�?phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.php��yj\�i��8phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php4 �yj\4 A;K2�:phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php,�yj\,cR�̶<phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.php��yj\� N�v�<phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.php�yj\��r�Aphpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.php��yj\�pb���<phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php� �yj\� �3��6phpspec-prophecy/Prophecy/Argument/Token/TypeToken.php��yj\��n�\�Bphpspec-prophecy/Prophecy/Argument/Token/ApproximateValueToken.php��yj\�#I��;phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.php��yj\��bN/�@phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.php��yj\������<phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.php��yj\��4�̶<phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.php��yj\��J�:�;phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.php��yj\�ٰ���@phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.php�yj\��|�:phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.php��yj\�F�h�=phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php9 -�yj\9 -E��.�Cphpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.php��yj\�����<phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.php��yj\��?Br�?phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.php�yj\�5�H�>phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.php��yj\�<�[�Aphpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.php��yj\�|��Aphpspec-prophecy/Prophecy/Doubler/Generator/TypeHintReference.php��yj\�t�>˶Bphpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php�yj\T�H�;phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.phpf�yj\fei�-phpspec-prophecy/Prophecy/Doubler/Doubler.php��yj\�8]�^�3phpspec-prophecy/Prophecy/Doubler/CachedDoubler.php��yj\�̇g�3phpspec-prophecy/Prophecy/Doubler/NameGenerator.php��yj\���7�5phpspec-prophecy/Prophecy/Doubler/DoubleInterface.php��yj\�8d�j�Hphpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.php��yj\�:0`�Cphpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.php��yj\�x��^�Dphpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phpl�yj\l)�5:�Ephpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php �yj\ ���Z�=phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php$ �yj\$ �AA��Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php �yj\ ��jN�?phpspec-prophecy/Prophecy/Doubler/ClassPatch/ThrowablePatch.phpa �yj\a �����Pphpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phpp�yj\px����Aphpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.php~ �yj\~ ��T��?phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.php -�yj\ -_aGk�0phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpF �yj\F ��l��&phpspec-prophecy/Prophecy/Argument.php��yj\�AT�;phpspec-prophecy/Prophecy/Comparator/ProphecyComparator.phps�yj\s��hǶ0phpspec-prophecy/Prophecy/Comparator/Factory.php��yj\�ֈi��:phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpK�yj\K)RQ�<phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php� �yj\� X���7phpspec-prophecy/Prophecy/Prediction/CallPrediction.phpQ �yj\Q I��;phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.php��yj\�Vb{ζ:phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.php��yj\�L9%�<phpspec-prophecy/Prophecy/Prediction/PredictionInterface.php��yj\�`IE�%phpspec-prophecy/Prophecy/Prophet.phpb�yj\b�Z`�Iphpspec-prophecy/Prophecy/PhpDocumentor/ClassAndInterfaceTagRetriever.phpx�yj\xrЬ�=phpspec-prophecy/Prophecy/PhpDocumentor/ClassTagRetriever.phpD�yj\Dd9�϶Gphpspec-prophecy/Prophecy/PhpDocumentor/MethodTagRetrieverInterface.php��yj\� �;��Cphpspec-prophecy/Prophecy/PhpDocumentor/LegacyClassTagRetriever.phpo�yj\ou9��-phpspec-prophecy/Prophecy/Util/ExportUtil.phpP�yj\P2�qƶ-phpspec-prophecy/Prophecy/Util/StringUtil.php� -�yj\� -�Z~��-phpspec-prophecy/Prophecy/Call/CallCenter.php*�yj\*�.w�'phpspec-prophecy/Prophecy/Call/Call.php� �yj\� 8Vuٶ3phpspec-prophecy/Prophecy/Promise/ReturnPromise.php�yj\�؏�6phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpK�yj\K����5phpspec-prophecy/Prophecy/Promise/CallbackPromise.php��yj\�[���;phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.php'�yj\'�(���2phpspec-prophecy/Prophecy/Promise/ThrowPromise.php] �yj\] �� ~�phpspec-prophecy/LICENSE}�yj\}��6�,sebastian-comparator/ExceptionComparator.php��yj\�ѕ���*sebastian-comparator/NumericComparator.php��yj\�=��Զ-sebastian-comparator/MockObjectComparator.phpP�yj\P�PN�'sebastian-comparator/TypeComparator.php��yj\�S��Ҷ)sebastian-comparator/DoubleComparator.phpt�yj\t�^k��#sebastian-comparator/Comparator.php\�yj\\8�9�(sebastian-comparator/ArrayComparator.php��yj\�Lj:Q�*sebastian-comparator/ComparisonFailure.php� �yj\� �I�+sebastian-comparator/ResourceComparator.php.�yj\.�K�� sebastian-comparator/Factory.php �yj\ o���*sebastian-comparator/DOMNodeComparator.php� -�yj\� -8���3sebastian-comparator/SplObjectStorageComparator.php��yj\� ��)sebastian-comparator/ObjectComparator.php�yj\�9�2�sebastian-comparator/LICENSE �yj\ �q��+sebastian-comparator/DateTimeComparator.php� �yj\� �~�ȶ)sebastian-comparator/ScalarComparator.php� �yj\� QM�theseer-tokenizer/Token.phpq�yj\q��?��#theseer-tokenizer/XMLSerializer.php� �yj\� E!{A�theseer-tokenizer/Exception.phpg�yj\gլ�X�%theseer-tokenizer/TokenCollection.phpr -�yj\r -�g9�+theseer-tokenizer/NamespaceUriException.phpr�yj\r+��A�.theseer-tokenizer/TokenCollectionException.phpu�yj\u��BK�theseer-tokenizer/Tokenizer.php��yj\��� ��theseer-tokenizer/LICENSE��yj\��R (�"theseer-tokenizer/NamespaceUri.phpw�yj\w 'U��'sebastian-recursion-context/Context.php{�yj\{����)sebastian-recursion-context/Exception.phpJ�yj\J����8sebastian-recursion-context/InvalidArgumentException.php��yj\�mH�#sebastian-recursion-context/LICENSE�yj\ ��>�php-text-template/Template.php� �yj\� �w4��php-text-template/LICENSE �yj\ S�:��object-enumerator/LICENSE�yj\�f�ζphpunit/Runner/Version.php��yj\�O�W��"phpunit/Runner/TestSuiteSorter.phpT*�yj\T*� ���*phpunit/Runner/StandardTestSuiteLoader.php �yj\ �\�'phpunit/Runner/ResultCacheExtension.php� �yj\� 9��9�&phpunit/Runner/Hook/BeforeTestHook.php��yj\�z�ݶ+phpunit/Runner/Hook/TestListenerAdapter.php��yj\����;�*phpunit/Runner/Hook/AfterTestErrorHook.php��yj\�LX�Ƕ,phpunit/Runner/Hook/AfterTestFailureHook.php��yj\�z�c��)phpunit/Runner/Hook/AfterLastTestHook.phpw�yj\w�~F�+phpunit/Runner/Hook/BeforeFirstTestHook.php{�yj\{�.�phpunit/Runner/Hook/Hook.php+�yj\+kXM��*phpunit/Runner/Hook/AfterRiskyTestHook.php��yj\�jY�M�/phpunit/Runner/Hook/AfterSuccessfulTestHook.php��yj\�j3���,phpunit/Runner/Hook/AfterTestWarningHook.php��yj\���ֆ�,phpunit/Runner/Hook/AfterSkippedTestHook.php��yj\��J �/phpunit/Runner/Hook/AfterIncompleteTestHook.php��yj\��*K� phpunit/Runner/Hook/TestHook.php<�yj\<xᤸ�%phpunit/Runner/Hook/AfterTestHook.phpQ�yj\Q����phpunit/Runner/Exception.phpK�yj\K@�5v�"phpunit/Runner/TestSuiteLoader.php�yj\XVE�phpunit/Runner/PhptTestCase.php>�yj\>fE�4phpunit/Runner/Filter/IncludeGroupFilterIterator.php��yj\�dL� �,phpunit/Runner/Filter/NameFilterIterator.php� �yj\� $��7�!phpunit/Runner/Filter/Factory.php��yj\���I��-phpunit/Runner/Filter/GroupFilterIterator.php �yj\ !��=�4phpunit/Runner/Filter/ExcludeGroupFilterIterator.php��yj\�,�8�!phpunit/Runner/BaseTestRunner.phpc�yj\c���&�phpunit/Exception.phpD�yj\D.�`�phpunit/TextUI/Command.php.��yj\.�TvD]�phpunit/TextUI/TestRunner.php���yj\��UC=� phpunit/TextUI/ResultPrinter.phpF=�yj\F=���!�7phpunit/Framework/TestListenerDefaultImplementation.phpn�yj\n"�� �2phpunit/Framework/InvalidCoversTargetException.phpG�yj\G�iZ��!phpunit/Framework/SkippedTest.php�yj\p��y�!phpunit/Framework/OutputError.php5�yj\5D���*phpunit/Framework/AssertionFailedError.php�yj\�9A��6phpunit/Framework/MissingCoversAnnotationException.phpD�yj\D�2l+�$phpunit/Framework/RiskyTestError.phpM�yj\Mie�"phpunit/Framework/Error/Notice.phpJ�yj\J�[�b�&phpunit/Framework/Error/Deprecated.phpN�yj\N,`,��!phpunit/Framework/Error/Error.phpa�yj\a��VԶ#phpunit/Framework/Error/Warning.phpK�yj\K]�W�"phpunit/Framework/TestListener.php��yj\�J �$phpunit/Framework/IncompleteTest.php��yj\��%�t�$phpunit/Framework/SyntheticError.php��yj\� ��p�)phpunit/Framework/IncompleteTestError.phpW�yj\WzY}+�phpunit/Framework/Assert.php!b�yj\!b�wt��phpunit/Framework/Exception.php �yj\ �K���%phpunit/Framework/SkippedTestCase.php��yj\�JH�phpunit/Framework/Test.php�yj\δP'�,phpunit/Framework/Constraint/JsonMatches.phpm �yj\m �BIw�,phpunit/Framework/Constraint/GreaterThan.php��yj\�?�2�)phpunit/Framework/Constraint/IsFinite.php�yj\M�_m�+phpunit/Framework/Constraint/IsReadable.phpE�yj\EX��T�+phpunit/Framework/Constraint/LogicalXor.php� �yj\� ��0�?phpunit/Framework/Constraint/StringMatchesFormatDescription.php` -�yj\` -�z���+phpunit/Framework/Constraint/IsInfinite.php�yj\�,��'phpunit/Framework/Constraint/IsType.php� �yj\� .jj�,phpunit/Framework/Constraint/ArrayHasKey.php��yj\�F�.phpunit/Framework/Constraint/ExceptionCode.php8�yj\8�^*R�-phpunit/Framework/Constraint/IsInstanceOf.php��yj\�����)phpunit/Framework/Constraint/SameSize.php��yj\���D�8phpunit/Framework/Constraint/TraversableContainsOnly.php[ �yj\[ &�x��)phpunit/Framework/Constraint/Callback.php�yj\ ���1phpunit/Framework/Constraint/ExceptionMessage.php.�yj\.�vH�0phpunit/Framework/Constraint/DirectoryExists.phpK�yj\KqS�A�)phpunit/Framework/Constraint/LessThan.php��yj\��&4/�3phpunit/Framework/Constraint/ObjectHasAttribute.php]�yj\]��OH�2phpunit/Framework/Constraint/RegularExpression.php<�yj\<6���*phpunit/Framework/Constraint/Exception.php��yj\����ܶ4phpunit/Framework/Constraint/TraversableContains.php_ �yj\_ :��ж/phpunit/Framework/Constraint/StringEndsWith.phpP�yj\P���'�(phpunit/Framework/Constraint/IsEqual.php��yj\���s��,phpunit/Framework/Constraint/ArraySubset.php��yj\�^�S �*phpunit/Framework/Constraint/Attribute.php �yj\ �E�+phpunit/Framework/Constraint/IsWritable.phpE�yj\E?�}ζ(phpunit/Framework/Constraint/IsEmpty.php��yj\��%n��1phpunit/Framework/Constraint/StringStartsWith.php=�yj\=;��1�(phpunit/Framework/Constraint/IsFalse.php�yj\�M��*phpunit/Framework/Constraint/Composite.php��yj\��>+�+phpunit/Framework/Constraint/Constraint.php8�yj\8r�8�*phpunit/Framework/Constraint/LogicalOr.php� �yj\� ���t�Bphpunit/Framework/Constraint/ExceptionMessageRegularExpression.php^�yj\^�b�&phpunit/Framework/Constraint/IsNan.php �yj\ �s�,phpunit/Framework/Constraint/IsIdentical.phpW�yj\W�����8phpunit/Framework/Constraint/ClassHasStaticAttribute.php��yj\��v�j�&phpunit/Framework/Constraint/Count.php� �yj\� ���*�+phpunit/Framework/Constraint/IsAnything.php��yj\�hu~�'phpunit/Framework/Constraint/IsNull.php�yj\�n��2phpunit/Framework/Constraint/ClassHasAttribute.php��yj\�.Tpo�+phpunit/Framework/Constraint/LogicalNot.php��yj\��EzӶ@phpunit/Framework/Constraint/JsonMatchesErrorMessageProvider.php!�yj\!X�۶'phpunit/Framework/Constraint/IsTrue.php�yj\D�/phpunit/Framework/Constraint/StringContains.php��yj\���I�'phpunit/Framework/Constraint/IsJson.php��yj\�h��/�+phpunit/Framework/Constraint/FileExists.php<�yj\<i� �+phpunit/Framework/Constraint/LogicalAnd.php �yj\ <���phpunit/Framework/TestCase.php��yj\���Aphpunit/Framework/MockObject/Exception/BadMethodCallException.phpc�yj\cu���;phpunit/Framework/MockObject/Exception/RuntimeException.phpW�yj\W[���4phpunit/Framework/MockObject/Exception/Exception.phpe�yj\e]Q⺶@phpunit/Framework/MockObject/ForwardCompatibility/MockObject.php��yj\���pd�1phpunit/Framework/MockObject/InvocationMocker.php��yj\���3�%phpunit/Framework/MockObject/Stub.phpY�yj\YPTЃ�+phpunit/Framework/MockObject/Verifiable.php��yj\�eA��;phpunit/Framework/MockObject/Generator/wsdl_method.tpl.dist<�yj\<��i��;phpunit/Framework/MockObject/Generator/deprecation.tpl.dist;�yj\;O5�s�Cphpunit/Framework/MockObject/Generator/mocked_class_method.tpl.dist��yj\��d��:phpunit/Framework/MockObject/Generator/wsdl_class.tpl.dist��yj\�w&S�;phpunit/Framework/MockObject/Generator/trait_class.tpl.dist7�yj\7�[$~�>phpunit/Framework/MockObject/Generator/proxied_method.tpl.dist��yj\�~D���Cphpunit/Framework/MockObject/Generator/proxied_method_void.tpl.dist��yj\�� 5_�Bphpunit/Framework/MockObject/Generator/mocked_method_void.tpl.dist&�yj\&�����<phpunit/Framework/MockObject/Generator/mocked_class.tpl.dist��yj\��ʷ��=phpunit/Framework/MockObject/Generator/mocked_method.tpl.dist]�yj\]~ӭ^�Dphpunit/Framework/MockObject/Generator/mocked_static_method.tpl.dist��yj\�w�Q��<phpunit/Framework/MockObject/Generator/mocked_clone.tpl.dist��yj\��aT�>phpunit/Framework/MockObject/Generator/unmocked_clone.tpl.dist��yj\�8W}ض6phpunit/Framework/MockObject/Invocation/Invocation.php��yj\��ȼ��<phpunit/Framework/MockObject/Invocation/StaticInvocation.phpG�yj\G�r��<phpunit/Framework/MockObject/Invocation/ObjectInvocation.php��yj\� ϿS�+phpunit/Framework/MockObject/MockMethod.phpN*�yj\N*7!�k�*phpunit/Framework/MockObject/Invokable.php��yj\���- -�6phpunit/Framework/MockObject/Matcher/AnyParameters.php��yj\�:I|�;phpunit/Framework/MockObject/Matcher/InvokedAtLeastOnce.phpf�yj\fh(l�3phpunit/Framework/MockObject/Matcher/Parameters.php��yj\�ڋ޴�<phpunit/Framework/MockObject/Matcher/StatelessInvocation.phpU�yj\U�@��3phpunit/Framework/MockObject/Matcher/MethodName.php��yj\�>Ȯ�7phpunit/Framework/MockObject/Matcher/InvokedAtIndex.php �yj\ `/n�5phpunit/Framework/MockObject/Matcher/InvokedCount.php� -�yj\� -a��8phpunit/Framework/MockObject/Matcher/InvokedRecorder.php:�yj\:I.W�>phpunit/Framework/MockObject/Matcher/ConsecutiveParameters.php�yj\2*H�8phpunit/Framework/MockObject/Matcher/AnyInvokedCount.phpV�yj\V��7�3phpunit/Framework/MockObject/Matcher/Invocation.php��yj\�w>�<phpunit/Framework/MockObject/Matcher/InvokedAtLeastCount.php��yj\���J��6phpunit/Framework/MockObject/Matcher/DeferredError.php*�yj\*=F���;phpunit/Framework/MockObject/Matcher/InvokedAtMostCount.php��yj\�� ��,phpunit/Framework/MockObject/MockBuilder.phpg�yj\g� i|�4phpunit/Framework/MockObject/Stub/ReturnArgument.php��yj\��WS�6phpunit/Framework/MockObject/Stub/ConsecutiveCalls.php��yj\��a��7phpunit/Framework/MockObject/Stub/MatcherCollection.php��yj\�岛�/phpunit/Framework/MockObject/Stub/Exception.php��yj\��ϙĶ4phpunit/Framework/MockObject/Stub/ReturnValueMap.php��yj\�Z�6�5phpunit/Framework/MockObject/Stub/ReturnReference.php��yj\��g�M�0phpunit/Framework/MockObject/Stub/ReturnStub.php��yj\����4phpunit/Framework/MockObject/Stub/ReturnCallback.phpf�yj\f��Ӷ0phpunit/Framework/MockObject/Stub/ReturnSelf.php��yj\��r@��.phpunit/Framework/MockObject/MockMethodSet.php<�yj\<Y��Ͷ(phpunit/Framework/MockObject/Matcher.php3"�yj\3"*ɠ��9phpunit/Framework/MockObject/Builder/InvocationMocker.phpF�yj\F� $��-phpunit/Framework/MockObject/Builder/Stub.php��yj\���)��7phpunit/Framework/MockObject/Builder/NamespaceMatch.php �yj\ N�q;�1phpunit/Framework/MockObject/Builder/Identity.phpZ�yj\Z�8z9�8phpunit/Framework/MockObject/Builder/ParametersMatch.php��yj\����Ķ.phpunit/Framework/MockObject/Builder/Match.php��yj\���ɶ8phpunit/Framework/MockObject/Builder/MethodNameMatch.php�yj\�xf��*phpunit/Framework/MockObject/Generator.phpm{�yj\m{� 1ж+phpunit/Framework/MockObject/MockObject.php,�yj\,�5�S�+phpunit/Framework/SkippedTestSuiteError.phpV�yj\VoϺ��phpunit/Framework/RiskyTest.php�yj\8c�}�(phpunit/Framework/IncompleteTestCase.php��yj\�,4�'phpunit/Framework/TestSuiteIterator.php��yj\��u o�$phpunit/Framework/SelfDescribing.php��yj\��S� phpunit/Framework/TestResult.php�s�yj\�s���j�5phpunit/Framework/CoveredCodeNotExecutedException.phpC�yj\CI��o�+phpunit/Framework/CodeCoverageException.php4�yj\4ψͶ+phpunit/Framework/DataProviderTestSuite.phpV�yj\VI�:��%phpunit/Framework/WarningTestCase.php��yj\��l�&phpunit/Framework/Assert/Functions.php4��yj\4�Q�`.�phpunit/Framework/TestSuite.phpOi�yj\OiczN�&phpunit/Framework/ExceptionWrapper.php� �yj\� �78�phpunit/Framework/Warning.php�yj\.�;�&phpunit/Framework/SkippedTestError.phpQ�yj\Q�E�0phpunit/Framework/ExpectationFailedException.php*�yj\*�a�!phpunit/Framework/TestFailure.php� �yj\� hۼö5phpunit/Framework/UnintentionallyCoveredCodeError.php��yj\��|��phpunit/Util/Xml.php��yj\���j̶'phpunit/Util/ConfigurationGenerator.php��yj\�lv.��phpunit/Util/GlobalState.php��yj\���$�'phpunit/Util/PHP/AbstractPhpProcess.php'&�yj\'&�&bL�&phpunit/Util/PHP/WindowsPhpProcess.phpY�yj\Y���&phpunit/Util/PHP/DefaultPhpProcess.php��yj\�!��R�phpunit/Util/PHP/eval-stdin.php�yj\�^�߶0phpunit/Util/PHP/Template/TestCaseClass.tpl.dist �yj\ /�Y�1phpunit/Util/PHP/Template/TestCaseMethod.tpl.distZ �yj\Z ���F�/phpunit/Util/PHP/Template/PhptTestCase.tpl.distb�yj\b���̶"phpunit/Util/RegularExpression.php��yj\�qċ�phpunit/Util/Test.php���yj\��)�j-�phpunit/Util/ErrorHandler.phpO �yj\O ;m�phpunit/Util/Filter.php� �yj\� ����*phpunit/Util/TestDox/HtmlResultPrinter.phpz -�yj\z -�8� �'phpunit/Util/TestDox/NamePrettifier.php��yj\��ʲ��*phpunit/Util/TestDox/TextResultPrinter.php4�yj\4�E���*phpunit/Util/TestDox/CliTestDoxPrinter.php|/�yj\|/��)<�#phpunit/Util/TestDox/TestResult.php��yj\��\3��)phpunit/Util/TestDox/XmlResultPrinter.php��yj\��6ݷ�&phpunit/Util/TestDox/ResultPrinter.phpc�yj\c�l�c�&phpunit/Util/InvalidArgumentHelper.php��yj\������$phpunit/Util/XmlTestListRenderer.php� �yj\� r��Ŷphpunit/Util/Getopt.php��yj\��}ֶphpunit/Util/Blacklist.php;�yj\;�-ݶ,phpunit/Util/XdebugFilterScriptGenerator.php!�yj\!P�1ɶphpunit/Util/Type.php%�yj\%:� �$phpunit/Util/NullTestResultCache.phpU�yj\Uf����phpunit/Util/Printer.php� �yj\� �xV��phpunit/Util/Log/TeamCity.php�)�yj\�)_ -&��phpunit/Util/Log/JUnit.php`-�yj\`-�;߶)phpunit/Util/TestResultCacheInterface.php��yj\�Vί#�phpunit/Util/Configuration.phpz��yj\z�؃ֶphpunit/Util/Json.php� �yj\� ��)��%phpunit/Util/TextTestListRenderer.php��yj\��(4� phpunit/Util/TestResultCache.phpz�yj\za�s��phpunit/Util/FileLoader.phpK�yj\K�AΑ�phpunit/Util/Filesystem.phpm�yj\m�k���Rdoctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.phpd�yj\d���-�Rdoctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.phpm �yj\m ���Ldoctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.php��yj\��.�öEdoctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php~�yj\~���:�<doctrine-instantiator/Doctrine/Instantiator/Instantiator.php�yj\��϶doctrine-instantiator/LICENSE$�yj\$ -͂�,phpdocumentor-reflection-common/Location.phpH�yj\H?-��+phpdocumentor-reflection-common/Element.php1�yj\1�iUҶ)phpdocumentor-reflection-common/Fqsen.php �yj\ ��Ӝ�(phpdocumentor-reflection-common/File.php7�yj\7�3"�'phpdocumentor-reflection-common/LICENSE9�yj\9*2Ȑ�+phpdocumentor-reflection-common/Project.php�yj\/H� �2phpdocumentor-reflection-common/ProjectFactory.php�yj\Q�"ܶ'sebastian-global-state/CodeExporter.phpf �yj\f |�!�#sebastian-global-state/Restorer.php"�yj\"M���#sebastian-global-state/Snapshot.php�!�yj\�!Ò���$sebastian-global-state/Blacklist.php� -�yj\� -ܫ9��6sebastian-global-state/exceptions/RuntimeException.php��yj\� �y˶/sebastian-global-state/exceptions/Exception.phpP�yj\PW�Z�sebastian-global-state/LICENSE�yj\q~Pd�:myclabs-deep-copy/DeepCopy/Exception/PropertyException.phpx�yj\x�4��7myclabs-deep-copy/DeepCopy/Exception/CloneException.php�yj\L��t�7myclabs-deep-copy/DeepCopy/TypeFilter/ReplaceFilter.php �yj\ 7���4myclabs-deep-copy/DeepCopy/TypeFilter/TypeFilter.php��yj\��-h�Amyclabs-deep-copy/DeepCopy/TypeFilter/Date/DateIntervalFilter.php�yj\GF�жGmyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php�yj\J�霶Amyclabs-deep-copy/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php��yj\���̶;myclabs-deep-copy/DeepCopy/TypeFilter/ShallowCopyFilter.php��yj\���Zٶ:myclabs-deep-copy/DeepCopy/Reflection/ReflectionHelper.php~�yj\~:�̶6myclabs-deep-copy/DeepCopy/TypeMatcher/TypeMatcher.php��yj\�s�wֶ:myclabs-deep-copy/DeepCopy/Matcher/PropertyNameMatcher.php��yj\�Y��x�Dmyclabs-deep-copy/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.phpo�yj\o?3�:myclabs-deep-copy/DeepCopy/Matcher/PropertyTypeMatcher.php&�yj\&��6myclabs-deep-copy/DeepCopy/Matcher/PropertyMatcher.php��yj\�?S�b�.myclabs-deep-copy/DeepCopy/Matcher/Matcher.php��yj\�q�e�3myclabs-deep-copy/DeepCopy/Filter/ReplaceFilter.php��yj\�a�˲�0myclabs-deep-copy/DeepCopy/Filter/KeepFilter.php�yj\����Lmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php��yj\���7߶Gmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php�yj\�:D�Bmyclabs-deep-copy/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php��yj\����,myclabs-deep-copy/DeepCopy/Filter/Filter.php\�yj\\6S���3myclabs-deep-copy/DeepCopy/Filter/SetNullFilter.php��yj\�]��]�'myclabs-deep-copy/DeepCopy/DeepCopy.php��yj\�a���(myclabs-deep-copy/DeepCopy/deep_copy.php��yj\�"e��myclabs-deep-copy/LICENSE5�yj\5ʭ˄� manifest.txtf�yj\ff�p�(sebastian-object-reflector/Exception.phpN�yj\N� ^�7sebastian-object-reflector/InvalidArgumentException.php��yj\�Y�J�.sebastian-object-reflector/ObjectReflector.php��yj\�� "�php-invoker/Exception.php@�yj\@'��php-invoker/Invoker.php��yj\� �̶ php-invoker/TimeoutException.phpx�yj\xI-��php-timer/Timer.php��yj\���ܲ�php-timer/RuntimeException.php|�yj\|�t�S�php-timer/Exception.phpD�yj\D�ɶphp-timer/LICENSE�yj\�����-sebastian-code-unit-reverse-lookup/Wizard.phpe �yj\e ����*sebastian-code-unit-reverse-lookup/LICENSE�yj\XX��.phpdocumentor-reflection-docblock/DocBlock.php�yj\��Z�5phpdocumentor-reflection-docblock/DocBlockFactory.php�$�yj\�$�Ń��<phpdocumentor-reflection-docblock/DocBlock/ExampleFinder.php��yj\��x��2phpdocumentor-reflection-docblock/DocBlock/Tag.phpu�yj\u⹰�Aphpdocumentor-reflection-docblock/DocBlock/DescriptionFactory.phpp�yj\p3��{�:phpdocumentor-reflection-docblock/DocBlock/Description.php/�yj\/>� �9phpdocumentor-reflection-docblock/DocBlock/Serializer.php��yj\�"~.0�9phpdocumentor-reflection-docblock/DocBlock/TagFactory.php�yj\P;Ͷ:phpdocumentor-reflection-docblock/DocBlock/Tags/Throws.php��yj\��Ȉض:phpdocumentor-reflection-docblock/DocBlock/Tags/Covers.phpF�yj\F.h�)�:phpdocumentor-reflection-docblock/DocBlock/Tags/Source.phpn �yj\n С���Aphpdocumentor-reflection-docblock/DocBlock/Tags/PropertyWrite.php� �yj\� s��ȶ;phpdocumentor-reflection-docblock/DocBlock/Tags/Return_.php��yj\��@��;phpdocumentor-reflection-docblock/DocBlock/Tags/Generic.phpX -�yj\X -D� �;phpdocumentor-reflection-docblock/DocBlock/Tags/Version.php� �yj\� �\�>phpdocumentor-reflection-docblock/DocBlock/Tags/Deprecated.php� -�yj\� -���Dphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/Strategy.php��yj\���R�Hphpdocumentor-reflection-docblock/DocBlock/Tags/Factory/StaticMethod.php��yj\��2i��Lphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/AlignFormatter.php��yj\������Rphpdocumentor-reflection-docblock/DocBlock/Tags/Formatter/PassthroughFormatter.php+�yj\+��ܶ;phpdocumentor-reflection-docblock/DocBlock/Tags/BaseTag.php��yj\�X� -c�;phpdocumentor-reflection-docblock/DocBlock/Tags/Example.phpp�yj\p�H���<phpdocumentor-reflection-docblock/DocBlock/Tags/Property.php� �yj\� ]]H��7phpdocumentor-reflection-docblock/DocBlock/Tags/See.php� �yj\� y%��@phpdocumentor-reflection-docblock/DocBlock/Tags/PropertyRead.php� �yj\� � 2��:phpdocumentor-reflection-docblock/DocBlock/Tags/Author.php� �yj\� ����9phpdocumentor-reflection-docblock/DocBlock/Tags/Since.php� �yj\� �yaö8phpdocumentor-reflection-docblock/DocBlock/Tags/Link.phpN�yj\NV���:phpdocumentor-reflection-docblock/DocBlock/Tags/Method.php��yj\�~�|��=phpdocumentor-reflection-docblock/DocBlock/Tags/Formatter.php��yj\�Dy7�9phpdocumentor-reflection-docblock/DocBlock/Tags/Param.php|�yj\|�8phpdocumentor-reflection-docblock/DocBlock/Tags/Var_.php �yj\ ��϶Aphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Url.phpM�yj\M���|�Cphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Fqsen.php��yj\���c�Gphpdocumentor-reflection-docblock/DocBlock/Tags/Reference/Reference.php,�yj\,�8(��8phpdocumentor-reflection-docblock/DocBlock/Tags/Uses.phpP�yj\PT,��Aphpdocumentor-reflection-docblock/DocBlock/StandardTagFactory.phpR.�yj\R.Ø��>phpdocumentor-reflection-docblock/DocBlockFactoryInterface.php!�yj\!��}�)phpdocumentor-reflection-docblock/LICENSE8�yj\8��ʶ3phar-io-manifest/xml/ComponentElementCollection.php�yj\#�Iζ9phar-io-manifest/xml/ManifestDocumentLoadingException.php��yj\����>�'phar-io-manifest/xml/LicenseElement.php4�yj\4�� �(phar-io-manifest/xml/RequiresElement.php�yj\(�ͷ�)phar-io-manifest/xml/CopyrightElement.php��yj\�����-phar-io-manifest/xml/ExtElementCollection.php�yj\����)phar-io-manifest/xml/ExtensionElement.phpB�yj\B����&phar-io-manifest/xml/AuthorElement.php7�yj\7���'phar-io-manifest/xml/BundlesElement.php&�yj\&Oϯ�)phar-io-manifest/xml/ComponentElement.php>�yj\> �+ȶ*phar-io-manifest/xml/ElementCollection.php��yj\��'�*�0phar-io-manifest/xml/AuthorElementCollection.php �yj\ B�yζ#phar-io-manifest/xml/PhpElement.php��yj\�p{:�#phar-io-manifest/xml/ExtElement.php��yj\�h/j3�(phar-io-manifest/xml/ManifestElement.php� �yj\� :6�X�)phar-io-manifest/xml/ManifestDocument.php� �yj\� ���1�(phar-io-manifest/xml/ContainsElement.php'�yj\'���Z�'phar-io-manifest/ManifestSerializer.php�yj\�tphar-io-manifest/values/BundledComponentCollectionIterator.php��yj\�}��'phar-io-manifest/values/Application.php��yj\�7$~׶,phar-io-manifest/values/BundledComponent.php��yj\��-�Ŷ1phar-io-manifest/values/RequirementCollection.php��yj\�)���#phar-io-manifest/values/License.php�yj\*�׶#phar-io-manifest/values/Library.php��yj\�F�z�'phar-io-manifest/values/Requirement.phpp�yj\p6V�A�"phar-io-manifest/values/Author.php%�yj\%��W8�1phar-io-manifest/values/PhpVersionRequirement.php�yj\�,��6phar-io-manifest/values/BundledComponentCollection.php(�yj\(� -�ڶ0phar-io-manifest/values/CopyrightInformation.phpx�yj\xt��b�%phar-io-manifest/values/Extension.php��yj\��0� phar-io-manifest/values/Type.php��yj\��pn��4phar-io-manifest/values/AuthorCollectionIterator.phpa�yj\a�����!phar-io-manifest/values/Email.php��yj\�c�M�9phar-io-manifest/values/RequirementCollectionIterator.php��yj\�n���,phar-io-manifest/values/AuthorCollection.php��yj\�Gr��+phar-io-manifest/values/ApplicationName.phpc�yj\c���8phar-io-manifest/exceptions/ManifestElementException.phpu�yj\u��w/�7phar-io-manifest/exceptions/ManifestLoaderException.phpm�yj\m8L:��5phar-io-manifest/exceptions/InvalidEmailException.php��yj\��3D�)phar-io-manifest/exceptions/Exception.phpn�yj\n��Y��?phar-io-manifest/exceptions/ManifestDocumentMapperException.php|�yj\|�A ��?phar-io-manifest/exceptions/InvalidApplicationNameException.php��yj\�6KQ�3phar-io-manifest/exceptions/InvalidUrlException.php��yj\�)���9phar-io-manifest/exceptions/ManifestDocumentException.phpv�yj\v��phar-io-manifest/LICENSEQ�yj\Q$W0�php-file-iterator/Iterator.php �yj\ �%�޶php-file-iterator/Facade.php� �yj\� k*�̶php-file-iterator/Factory.php��yj\��l Ҷphp-file-iterator/LICENSE�yj\J�U(�php-token-stream/Token.php-a�yj\-at̯�0php-token-stream/Token/Stream/CachingFactory.php��yj\� \h��!php-token-stream/Token/Stream.php�>�yj\�>����php-token-stream/LICENSE�yj\ -}� phpunit.xsdJ@�yj\J@��{�webmozart-assert/Assert.php���yj\��ހ�webmozart-assert/LICENSE<�yj\<t�}��!sebastian-environment/Console.php��yj\�,���!sebastian-environment/Runtime.php&�yj\&j�4�)sebastian-environment/OperatingSystem.php��yj\��`���sebastian-environment/LICENSE�yj\I���)sebastian-object-enumerator/Exception.php6�yj\6n$*a�*sebastian-object-enumerator/Enumerator.phpr�yj\rz�\��8sebastian-object-enumerator/InvalidArgumentException.phpx�yj\x��'��sebastian-version/Version.php��yj\�N\Ƕsebastian-version/LICENSE�yj\n�3sebastian-diff/Exception/ConfigurationException.phpC�yj\C<� 1�&sebastian-diff/Exception/Exception.php@�yj\@g�ն5sebastian-diff/Exception/InvalidArgumentException.php��yj\�g����Dsebastian-diff/MemoryEfficientLongestCommonSubsequenceCalculator.phpV�yj\VfնBsebastian-diff/TimeEfficientLongestCommonSubsequenceCalculator.php �yj\ v�<�sebastian-diff/Line.phpO�yj\O'�� �sebastian-diff/Differ.php9%�yj\9%��D�4sebastian-diff/Output/DiffOutputBuilderInterface.php -�yj\ -�zk�8sebastian-diff/Output/StrictUnifiedDiffOutputBuilder.php�(�yj\�(���N�4sebastian-diff/Output/AbstractChunkOutputBuilder.phpO�yj\OE�l�2sebastian-diff/Output/UnifiedDiffOutputBuilder.php* �yj\* ����/sebastian-diff/Output/DiffOnlyOutputBuilder.php��yj\��28>�sebastian-diff/Diff.php��yj\�3߬�sebastian-diff/Parser.php� �yj\� G�2�sebastian-diff/Chunk.phpm�yj\m�A��5sebastian-diff/LongestCommonSubsequenceCalculator.php<�yj\<����sebastian-diff/LICENSE �yj\ Dte#�@php-code-coverage/Exception/MissingCoversAnnotationException.php��yj\��u���0php-code-coverage/Exception/RuntimeException.phpo�yj\oH���)php-code-coverage/Exception/Exception.php}�yj\}=�0:�?php-code-coverage/Exception/CoveredCodeNotExecutedException.php��yj\��߁�8php-code-coverage/Exception/InvalidArgumentException.php�yj\ �ؕ�Cphp-code-coverage/Exception/UnintentionallyCoveredCodeException.php4�yj\43�ٶ#php-code-coverage/Report/Clover.php�(�yj\�(�+.� php-code-coverage/Report/PHP.phpJ�yj\JM� ��&php-code-coverage/Report/Xml/Tests.phpl�yj\l�^�W�*php-code-coverage/Report/Xml/Directory.phpW�yj\WS��˶'php-code-coverage/Report/Xml/Source.php��yj\���G�'php-code-coverage/Report/Xml/Totals.php��yj\�m�*��)php-code-coverage/Report/Xml/Coverage.php��yj\�tw�}�'php-code-coverage/Report/Xml/Report.php� �yj\� �� ��'php-code-coverage/Report/Xml/Method.php��yj\���"n�'php-code-coverage/Report/Xml/Facade.phpd �yj\d c��k�%php-code-coverage/Report/Xml/Unit.php} -�yj\} -��s��1php-code-coverage/Report/Xml/BuildInformation.php��yj\�Mn%�%php-code-coverage/Report/Xml/File.php��yj\��P��%php-code-coverage/Report/Xml/Node.phpM�yj\M�W��(php-code-coverage/Report/Xml/Project.php$ �yj\$ @/�_�4php-code-coverage/Report/Html/Renderer/Directory.php� �yj\� !2<�4php-code-coverage/Report/Html/Renderer/Dashboard.phpW%�yj\W%���)�Hphp-code-coverage/Report/Html/Renderer/Template/icons/file-directory.svg��yj\���Z��Cphp-code-coverage/Report/Html/Renderer/Template/icons/file-code.svg0�yj\0�QUU�@php-code-coverage/Report/Html/Renderer/Template/js/popper.min.jsqO�yj\qO�v/Ͷ:php-code-coverage/Report/Html/Renderer/Template/js/file.js��yj\��'��?php-code-coverage/Report/Html/Renderer/Template/js/nv.d3.min.js�R�yj\�Rphp-code-coverage/Report/Html/Renderer/Template/file.html.dist -�yj\ -�ɳ�Fphp-code-coverage/Report/Html/Renderer/Template/coverage_bar.html.dist'�yj\'�O}�Cphp-code-coverage/Report/Html/Renderer/Template/dashboard.html.distG�yj\G�K〶Cphp-code-coverage/Report/Html/Renderer/Template/directory.html.dist��yj\�hG���Ephp-code-coverage/Report/Html/Renderer/Template/method_item.html.dist��yj\���s:�@php-code-coverage/Report/Html/Renderer/Template/css/octicons.cssX�yj\X'#��>php-code-coverage/Report/Html/Renderer/Template/css/custom.css�yj\�Ephp-code-coverage/Report/Html/Renderer/Template/css/bootstrap.min.css�&�yj\�&1�L�=php-code-coverage/Report/Html/Renderer/Template/css/style.css��yj\���v�Aphp-code-coverage/Report/Html/Renderer/Template/css/nv.d3.min.cssX%�yj\X%�0,�/php-code-coverage/Report/Html/Renderer/File.php�I�yj\�IF�fж(php-code-coverage/Report/Html/Facade.php6�yj\6t��?�*php-code-coverage/Report/Html/Renderer.php� �yj\� {,(�!php-code-coverage/Report/Text.phpR"�yj\R"���#php-code-coverage/Report/Crap4j.php�yj\�q$�"php-code-coverage/CodeCoverage.phpar�yj\ar|����php-code-coverage/Version.php��yj\��� -$�#php-code-coverage/Driver/PHPDBG.phpu -�yj\u -� ��#php-code-coverage/Driver/Xdebug.php� -�yj\� -p:���#php-code-coverage/Driver/Driver.php��yj\�.�߸�php-code-coverage/Util.phpM�yj\MXq逶#php-code-coverage/Node/Iterator.php!�yj\!0n�ܶ$php-code-coverage/Node/Directory.phpX$�yj\X${��$�"php-code-coverage/Node/Builder.phpt�yj\t�o�/�php-code-coverage/Node/File.php@�yj\@ `�o�'php-code-coverage/Node/AbstractNode.php5�yj\5=K�ʶphp-code-coverage/Filter.phpl�yj\l� -`��php-code-coverage/LICENSE�yj\yM�F�sebastian-exporter/LICENSE�yj\��`�sebastian-exporter/Exporter.php�#�yj\�#!�Y��!phar-io-version/VersionNumber.php"�yj\"�v�޶phar-io-version/Version.php^�yj\^[A+�*phar-io-version/VersionConstraintValue.php -�yj\ -���>phar-io-version/constraints/SpecificMajorVersionConstraint.phpu�yj\ur��8phar-io-version/constraints/OrVersionConstraintGroup.php(�yj\(����Fphar-io-version/constraints/SpecificMajorAndMinorVersionConstraint.php[�yj\[@��n�9phar-io-version/constraints/AndVersionConstraintGroup.php*�yj\* C�h�9phar-io-version/constraints/AbstractVersionConstraint.php��yj\�Whg��6phar-io-version/constraints/ExactVersionConstraint.phpZ�yj\Z ���1phar-io-version/constraints/VersionConstraint.php6�yj\6w�U��Ephar-io-version/constraints/GreaterThanOrEqualToVersionConstraint.php �yj\ � �4phar-io-version/constraints/AnyVersionConstraint.php��yj\����+phar-io-version/VersionConstraintParser.php� �yj\� ^�.�?phar-io-version/exceptions/InvalidPreReleaseSuffixException.phpv�yj\v����(phar-io-version/exceptions/Exception.phpl�yj\l�؈�6phar-io-version/exceptions/InvalidVersionException.php{�yj\{O��Dphar-io-version/exceptions/UnsupportedVersionConstraintException.php��yj\��`r�phar-io-version/LICENSE1�yj\1>��:�$phar-io-version/PreReleaseSuffix.phpg�yj\g��d��4sebastian-resource-operations/ResourceOperations.phpi�yj\i6G�%sebastian-resource-operations/LICENSE�yj\��r� - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -use phpDocumentor\Reflection\Types\Context; - -class FqsenResolver -{ - /** @var string Definition of the NAMESPACE operator in PHP */ - const OPERATOR_NAMESPACE = '\\'; - - public function resolve($fqsen, Context $context = null) - { - if ($context === null) { - $context = new Context(''); - } - - if ($this->isFqsen($fqsen)) { - return new Fqsen($fqsen); - } - - return $this->resolvePartialStructuralElementName($fqsen, $context); - } - - /** - * Tests whether the given type is a Fully Qualified Structural Element Name. - * - * @param string $type - * - * @return bool - */ - private function isFqsen($type) - { - return strpos($type, self::OPERATOR_NAMESPACE) === 0; - } - - /** - * Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation - * (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context. - * - * @param string $type - * @param Context $context - * - * @return Fqsen - * @throws \InvalidArgumentException when type is not a valid FQSEN. - */ - private function resolvePartialStructuralElementName($type, Context $context) - { - $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); - - $namespaceAliases = $context->getNamespaceAliases(); - - // if the first segment is not an alias; prepend namespace name and return - if (!isset($namespaceAliases[$typeParts[0]])) { - $namespace = $context->getNamespace(); - if ('' !== $namespace) { - $namespace .= self::OPERATOR_NAMESPACE; - } - - return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); - } - - $typeParts[0] = $namespaceAliases[$typeParts[0]]; - - return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts)); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -interface Type -{ - public function __toString(); -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'resource' Type. - */ -final class Resource_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'resource'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -/** - * Provides information about the Context in which the DocBlock occurs that receives this context. - * - * A DocBlock does not know of its own accord in which namespace it occurs and which namespace aliases are applicable - * for the block of code in which it is in. This information is however necessary to resolve Class names in tags since - * you can provide a short form or make use of namespace aliases. - * - * The phpDocumentor Reflection component knows how to create this class but if you use the DocBlock parser from your - * own application it is possible to generate a Context class using the ContextFactory; this will analyze the file in - * which an associated class resides for its namespace and imports. - * - * @see ContextFactory::createFromClassReflector() - * @see ContextFactory::createForNamespace() - */ -final class Context -{ - /** @var string The current namespace. */ - private $namespace; - - /** @var array List of namespace aliases => Fully Qualified Namespace. */ - private $namespaceAliases; - - /** - * Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN) - * format (without a preceding `\`). - * - * @param string $namespace The namespace where this DocBlock resides in. - * @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace. - */ - public function __construct($namespace, array $namespaceAliases = []) - { - $this->namespace = ('global' !== $namespace && 'default' !== $namespace) - ? trim((string)$namespace, '\\') - : ''; - - foreach ($namespaceAliases as $alias => $fqnn) { - if ($fqnn[0] === '\\') { - $fqnn = substr($fqnn, 1); - } - if ($fqnn[strlen($fqnn) - 1] === '\\') { - $fqnn = substr($fqnn, 0, -1); - } - - $namespaceAliases[$alias] = $fqnn; - } - - $this->namespaceAliases = $namespaceAliases; - } - - /** - * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. - * - * @return string - */ - public function getNamespace() - { - return $this->namespace; - } - - /** - * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent - * the alias for the imported Namespace. - * - * @return string[] - */ - public function getNamespaceAliases() - { - return $this->namespaceAliases; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean. - */ -final class Scalar implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'scalar'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Boolean type. - */ -final class Boolean implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'bool'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the type 'string'. - */ -final class String_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'string'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'parent' type. - * - * Parent, as a Type, represents the parent class of class in which the associated element was defined. - */ -final class Parent_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'parent'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'self' type. - * - * Self, as a Type, represents the class in which the associated element was defined. - */ -final class Self_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'self'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a nullable type. The real type is wrapped. - */ -final class Nullable implements Type -{ - /** - * @var Type - */ - private $realType; - - /** - * Initialises this nullable type using the real type embedded - * - * @param Type $realType - */ - public function __construct(Type $realType) - { - $this->realType = $realType; - } - - /** - * Provide access to the actual type directly, if needed. - * - * @return Type - */ - public function getActualType() - { - return $this->realType; - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return '?' . $this->realType->__toString(); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use ArrayIterator; -use IteratorAggregate; -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Compound Type. - * - * A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated - * using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type - * may contain a value with any of the given types. - */ -final class Compound implements Type, IteratorAggregate -{ - /** @var Type[] */ - private $types; - - /** - * Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface. - * - * @param Type[] $types - * @throws \InvalidArgumentException when types are not all instance of Type - */ - public function __construct(array $types) - { - foreach ($types as $type) { - if (!$type instanceof Type) { - throw new \InvalidArgumentException('A compound type can only have other types as elements'); - } - } - - $this->types = $types; - } - - /** - * Returns the type at the given index. - * - * @param integer $index - * - * @return Type|null - */ - public function get($index) - { - if (!$this->has($index)) { - return null; - } - - return $this->types[$index]; - } - - /** - * Tests if this compound type has a type with the given index. - * - * @param integer $index - * - * @return bool - */ - public function has($index) - { - return isset($this->types[$index]); - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return implode('|', $this->types); - } - - /** - * {@inheritdoc} - */ - public function getIterator() - { - return new ArrayIterator($this->types); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing iterable type - */ -final class Iterable_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'iterable'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the pseudo-type 'void'. - * - * Void is generally only used when working with return types as it signifies that the method intentionally does not - * return any value. - */ -final class Void_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'void'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Callable type. - */ -final class Callable_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'callable'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing an unknown, or mixed, type. - */ -final class Mixed_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'mixed'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Fqsen; -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing an object. - * - * An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN, - * pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects - * in general. - */ -final class Object_ implements Type -{ - /** @var Fqsen|null */ - private $fqsen; - - /** - * Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'. - * - * @param Fqsen $fqsen - * @throws \InvalidArgumentException when provided $fqsen is not a valid type. - */ - public function __construct(Fqsen $fqsen = null) - { - if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) { - throw new \InvalidArgumentException( - 'Object types can only refer to a class, interface or trait but a method, function, constant or ' - . 'property was received: ' . (string)$fqsen - ); - } - - $this->fqsen = $fqsen; - } - - /** - * Returns the FQSEN associated with this object. - * - * @return Fqsen|null - */ - public function getFqsen() - { - return $this->fqsen; - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - if ($this->fqsen) { - return (string)$this->fqsen; - } - - return 'object'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a Float. - */ -final class Float_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'float'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -final class Integer implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'int'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the '$this' pseudo-type. - * - * $this, as a Type, represents the instance of the class associated with the element as it was called. $this is - * commonly used when documenting fluent interfaces since it represents that the same object is returned. - */ -final class This implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return '$this'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Represents an array type as described in the PSR-5, the PHPDoc Standard. - * - * An array can be represented in two forms: - * - * 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'. - * 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a - * type name. - */ -final class Array_ implements Type -{ - /** @var Type */ - private $valueType; - - /** @var Type */ - private $keyType; - - /** - * Initializes this representation of an array with the given Type or Fqsen. - * - * @param Type $valueType - * @param Type $keyType - */ - public function __construct(Type $valueType = null, Type $keyType = null) - { - if ($keyType === null) { - $keyType = new Compound([ new String_(), new Integer() ]); - } - if ($valueType === null) { - $valueType = new Mixed_(); - } - - $this->valueType = $valueType; - $this->keyType = $keyType; - } - - /** - * Returns the type for the keys of this array. - * - * @return Type - */ - public function getKeyType() - { - return $this->keyType; - } - - /** - * Returns the value for the keys of this array. - * - * @return Type - */ - public function getValueType() - { - return $this->valueType; - } - - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - if ($this->valueType instanceof Mixed_) { - return 'array'; - } - - return $this->valueType . '[]'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing the 'static' type. - * - * Self, as a Type, represents the class in which the associated element was called. This differs from self as self does - * not take inheritance into account but static means that the return type is always that of the class of the called - * element. - * - * See the documentation on late static binding in the PHP Documentation for more information on the difference between - * static and self. - */ -final class Static_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'static'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -/** - * Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor. - * - * For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to - * provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to - * Fully Qualified names. - * - * @see Context for more information. - */ -final class ContextFactory -{ - /** The literal used at the end of a use statement. */ - const T_LITERAL_END_OF_USE = ';'; - - /** The literal used between sets of use statements */ - const T_LITERAL_USE_SEPARATOR = ','; - - /** - * Build a Context given a Class Reflection. - * - * @param \Reflector $reflector - * - * @see Context for more information on Contexts. - * - * @return Context - */ - public function createFromReflector(\Reflector $reflector) - { - if (method_exists($reflector, 'getDeclaringClass')) { - $reflector = $reflector->getDeclaringClass(); - } - - $fileName = $reflector->getFileName(); - $namespace = $reflector->getNamespaceName(); - - if (file_exists($fileName)) { - return $this->createForNamespace($namespace, file_get_contents($fileName)); - } - - return new Context($namespace, []); - } - - /** - * Build a Context for a namespace in the provided file contents. - * - * @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes. - * @param string $fileContents the file's contents to retrieve the aliases from with the given namespace. - * - * @see Context for more information on Contexts. - * - * @return Context - */ - public function createForNamespace($namespace, $fileContents) - { - $namespace = trim($namespace, '\\'); - $useStatements = []; - $currentNamespace = ''; - $tokens = new \ArrayIterator(token_get_all($fileContents)); - - while ($tokens->valid()) { - switch ($tokens->current()[0]) { - case T_NAMESPACE: - $currentNamespace = $this->parseNamespace($tokens); - break; - case T_CLASS: - // Fast-forward the iterator through the class so that any - // T_USE tokens found within are skipped - these are not - // valid namespace use statements so should be ignored. - $braceLevel = 0; - $firstBraceFound = false; - while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { - if ($tokens->current() === '{' - || $tokens->current()[0] === T_CURLY_OPEN - || $tokens->current()[0] === T_DOLLAR_OPEN_CURLY_BRACES) { - if (!$firstBraceFound) { - $firstBraceFound = true; - } - $braceLevel++; - } - - if ($tokens->current() === '}') { - $braceLevel--; - } - $tokens->next(); - } - break; - case T_USE: - if ($currentNamespace === $namespace) { - $useStatements = array_merge($useStatements, $this->parseUseStatement($tokens)); - } - break; - } - $tokens->next(); - } - - return new Context($namespace, $useStatements); - } - - /** - * Deduce the name from tokens when we are at the T_NAMESPACE token. - * - * @param \ArrayIterator $tokens - * - * @return string - */ - private function parseNamespace(\ArrayIterator $tokens) - { - // skip to the first string or namespace separator - $this->skipToNextStringOrNamespaceSeparator($tokens); - - $name = ''; - while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) - ) { - $name .= $tokens->current()[1]; - $tokens->next(); - } - - return $name; - } - - /** - * Deduce the names of all imports when we are at the T_USE token. - * - * @param \ArrayIterator $tokens - * - * @return string[] - */ - private function parseUseStatement(\ArrayIterator $tokens) - { - $uses = []; - $continue = true; - - while ($continue) { - $this->skipToNextStringOrNamespaceSeparator($tokens); - - list($alias, $fqnn) = $this->extractUseStatement($tokens); - $uses[$alias] = $fqnn; - if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) { - $continue = false; - } - } - - return $uses; - } - - /** - * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. - * - * @param \ArrayIterator $tokens - * - * @return void - */ - private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $tokens) - { - while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) { - $tokens->next(); - } - } - - /** - * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of - * a USE statement yet. - * - * @param \ArrayIterator $tokens - * - * @return string - */ - private function extractUseStatement(\ArrayIterator $tokens) - { - $result = ['']; - while ($tokens->valid() - && ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR) - && ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE) - ) { - if ($tokens->current()[0] === T_AS) { - $result[] = ''; - } - if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) { - $result[count($result) - 1] .= $tokens->current()[1]; - } - $tokens->next(); - } - - if (count($result) == 1) { - $backslashPos = strrpos($result[0], '\\'); - - if (false !== $backslashPos) { - $result[] = substr($result[0], $backslashPos + 1); - } else { - $result[] = $result[0]; - } - } - - return array_reverse($result); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\Types; - -use phpDocumentor\Reflection\Type; - -/** - * Value Object representing a null value or type. - */ -final class Null_ implements Type -{ - /** - * Returns a rendered output of the Type as it would be used in a DocBlock. - * - * @return string - */ - public function __toString() - { - return 'null'; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -use phpDocumentor\Reflection\Types\Array_; -use phpDocumentor\Reflection\Types\Compound; -use phpDocumentor\Reflection\Types\Context; -use phpDocumentor\Reflection\Types\Iterable_; -use phpDocumentor\Reflection\Types\Nullable; -use phpDocumentor\Reflection\Types\Object_; - -final class TypeResolver -{ - /** @var string Definition of the ARRAY operator for types */ - const OPERATOR_ARRAY = '[]'; - - /** @var string Definition of the NAMESPACE operator in PHP */ - const OPERATOR_NAMESPACE = '\\'; - - /** @var string[] List of recognized keywords and unto which Value Object they map */ - private $keywords = array( - 'string' => Types\String_::class, - 'int' => Types\Integer::class, - 'integer' => Types\Integer::class, - 'bool' => Types\Boolean::class, - 'boolean' => Types\Boolean::class, - 'float' => Types\Float_::class, - 'double' => Types\Float_::class, - 'object' => Object_::class, - 'mixed' => Types\Mixed_::class, - 'array' => Array_::class, - 'resource' => Types\Resource_::class, - 'void' => Types\Void_::class, - 'null' => Types\Null_::class, - 'scalar' => Types\Scalar::class, - 'callback' => Types\Callable_::class, - 'callable' => Types\Callable_::class, - 'false' => Types\Boolean::class, - 'true' => Types\Boolean::class, - 'self' => Types\Self_::class, - '$this' => Types\This::class, - 'static' => Types\Static_::class, - 'parent' => Types\Parent_::class, - 'iterable' => Iterable_::class, - ); - - /** @var FqsenResolver */ - private $fqsenResolver; - - /** - * Initializes this TypeResolver with the means to create and resolve Fqsen objects. - * - * @param FqsenResolver $fqsenResolver - */ - public function __construct(FqsenResolver $fqsenResolver = null) - { - $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); - } - - /** - * Analyzes the given type and returns the FQCN variant. - * - * When a type is provided this method checks whether it is not a keyword or - * Fully Qualified Class Name. If so it will use the given namespace and - * aliases to expand the type to a FQCN representation. - * - * This method only works as expected if the namespace and aliases are set; - * no dynamic reflection is being performed here. - * - * @param string $type The relative or absolute type. - * @param Context $context - * - * @uses Context::getNamespace() to determine with what to prefix the type name. - * @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be - * replaced with another namespace. - * - * @return Type|null - */ - public function resolve($type, Context $context = null) - { - if (!is_string($type)) { - throw new \InvalidArgumentException( - 'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true) - ); - } - - $type = trim($type); - if (!$type) { - throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); - } - - if ($context === null) { - $context = new Context(''); - } - - switch (true) { - case $this->isNullableType($type): - return $this->resolveNullableType($type, $context); - case $this->isKeyword($type): - return $this->resolveKeyword($type); - case ($this->isCompoundType($type)): - return $this->resolveCompoundType($type, $context); - case $this->isTypedArray($type): - return $this->resolveTypedArray($type, $context); - case $this->isFqsen($type): - return $this->resolveTypedObject($type); - case $this->isPartialStructuralElementName($type): - return $this->resolveTypedObject($type, $context); - // @codeCoverageIgnoreStart - default: - // I haven't got the foggiest how the logic would come here but added this as a defense. - throw new \RuntimeException( - 'Unable to resolve type "' . $type . '", there is no known method to resolve it' - ); - } - // @codeCoverageIgnoreEnd - } - - /** - * Adds a keyword to the list of Keywords and associates it with a specific Value Object. - * - * @param string $keyword - * @param string $typeClassName - * - * @return void - */ - public function addKeyword($keyword, $typeClassName) - { - if (!class_exists($typeClassName)) { - throw new \InvalidArgumentException( - 'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class' - . ' but we could not find the class ' . $typeClassName - ); - } - - if (!in_array(Type::class, class_implements($typeClassName))) { - throw new \InvalidArgumentException( - 'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"' - ); - } - - $this->keywords[$keyword] = $typeClassName; - } - - /** - * Detects whether the given type represents an array. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @return bool - */ - private function isTypedArray($type) - { - return substr($type, -2) === self::OPERATOR_ARRAY; - } - - /** - * Detects whether the given type represents a PHPDoc keyword. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @return bool - */ - private function isKeyword($type) - { - return in_array(strtolower($type), array_keys($this->keywords), true); - } - - /** - * Detects whether the given type represents a relative structural element name. - * - * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. - * - * @return bool - */ - private function isPartialStructuralElementName($type) - { - return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type); - } - - /** - * Tests whether the given type is a Fully Qualified Structural Element Name. - * - * @param string $type - * - * @return bool - */ - private function isFqsen($type) - { - return strpos($type, self::OPERATOR_NAMESPACE) === 0; - } - - /** - * Tests whether the given type is a compound type (i.e. `string|int`). - * - * @param string $type - * - * @return bool - */ - private function isCompoundType($type) - { - return strpos($type, '|') !== false; - } - - /** - * Test whether the given type is a nullable type (i.e. `?string`) - * - * @param string $type - * - * @return bool - */ - private function isNullableType($type) - { - return $type[0] === '?'; - } - - /** - * Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set. - * - * @param string $type - * @param Context $context - * - * @return Array_ - */ - private function resolveTypedArray($type, Context $context) - { - return new Array_($this->resolve(substr($type, 0, -2), $context)); - } - - /** - * Resolves the given keyword (such as `string`) into a Type object representing that keyword. - * - * @param string $type - * - * @return Type - */ - private function resolveKeyword($type) - { - $className = $this->keywords[strtolower($type)]; - - return new $className(); - } - - /** - * Resolves the given FQSEN string into an FQSEN object. - * - * @param string $type - * @param Context|null $context - * - * @return Object_ - */ - private function resolveTypedObject($type, Context $context = null) - { - return new Object_($this->fqsenResolver->resolve($type, $context)); - } - - /** - * Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN. - * - * @param string $type - * @param Context $context - * - * @return Compound - */ - private function resolveCompoundType($type, Context $context) - { - $types = []; - - foreach (explode('|', $type) as $part) { - $types[] = $this->resolve($part, $context); - } - - return new Compound($types); - } - - /** - * Resolve nullable types (i.e. `?string`) into a Nullable type wrapper - * - * @param string $type - * @param Context $context - * - * @return Nullable - */ - private function resolveNullableType($type, Context $context) - { - return new Nullable($this->resolve(ltrim($type, '?'), $context)); - } -} -The MIT License (MIT) - -Copyright (c) 2010 Mike van Riel - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -Object Reflector - -Copyright (c) 2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prophecy; - -use Prophecy\Prophecy\ObjectProphecy; - -class ObjectProphecyException extends \RuntimeException implements ProphecyException -{ - private $objectProphecy; - - public function __construct($message, ObjectProphecy $objectProphecy) - { - parent::__construct($message); - - $this->objectProphecy = $objectProphecy; - } - - /** - * @return ObjectProphecy - */ - public function getObjectProphecy() - { - return $this->objectProphecy; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prophecy; - -use Prophecy\Exception\Exception; - -interface ProphecyException extends Exception -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prophecy; - -use Prophecy\Prophecy\MethodProphecy; - -class MethodProphecyException extends ObjectProphecyException -{ - private $methodProphecy; - - public function __construct($message, MethodProphecy $methodProphecy) - { - parent::__construct($message, $methodProphecy->getObjectProphecy()); - - $this->methodProphecy = $methodProphecy; - } - - /** - * @return MethodProphecy - */ - public function getMethodProphecy() - { - return $this->methodProphecy; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -class ClassNotFoundException extends DoubleException -{ - private $classname; - - /** - * @param string $message - * @param string $classname - */ - public function __construct($message, $classname) - { - parent::__construct($message); - - $this->classname = $classname; - } - - public function getClassname() - { - return $this->classname; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -use Prophecy\Doubler\Generator\Node\ClassNode; - -class ClassCreatorException extends \RuntimeException implements DoublerException -{ - private $node; - - public function __construct($message, ClassNode $node) - { - parent::__construct($message); - - $this->node = $node; - } - - public function getClassNode() - { - return $this->node; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -class InterfaceNotFoundException extends ClassNotFoundException -{ - public function getInterfaceName() - { - return $this->getClassname(); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -class ReturnByReferenceException extends DoubleException -{ - private $classname; - private $methodName; - - /** - * @param string $message - * @param string $classname - * @param string $methodName - */ - public function __construct($message, $classname, $methodName) - { - parent::__construct($message); - - $this->classname = $classname; - $this->methodName = $methodName; - } - - public function getClassname() - { - return $this->classname; - } - - public function getMethodName() - { - return $this->methodName; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -use RuntimeException; - -class DoubleException extends RuntimeException implements DoublerException -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -use ReflectionClass; - -class ClassMirrorException extends \RuntimeException implements DoublerException -{ - private $class; - - public function __construct($message, ReflectionClass $class) - { - parent::__construct($message); - - $this->class = $class; - } - - public function getReflectedClass() - { - return $this->class; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -class MethodNotFoundException extends DoubleException -{ - /** - * @var string|object - */ - private $classname; - - /** - * @var string - */ - private $methodName; - - /** - * @var array - */ - private $arguments; - - /** - * @param string $message - * @param string|object $classname - * @param string $methodName - * @param null|Argument\ArgumentsWildcard|array $arguments - */ - public function __construct($message, $classname, $methodName, $arguments = null) - { - parent::__construct($message); - - $this->classname = $classname; - $this->methodName = $methodName; - $this->arguments = $arguments; - } - - public function getClassname() - { - return $this->classname; - } - - public function getMethodName() - { - return $this->methodName; - } - - public function getArguments() - { - return $this->arguments; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Doubler; - -use Prophecy\Exception\Exception; - -interface DoublerException extends Exception -{ -} -methodName = $methodName; - $this->className = $className; - } - - - /** - * @return string - */ - public function getMethodName() - { - return $this->methodName; - } - - /** - * @return string - */ - public function getClassName() - { - return $this->className; - } - - } - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception; - -/** - * Core Prophecy exception interface. - * All Prophecy exceptions implement it. - * - * @author Konstantin Kudryashov - */ -interface Exception -{ - /** - * @return string - */ - public function getMessage(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception; - -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prediction; - -use Prophecy\Prophecy\ObjectProphecy; - -class AggregateException extends \RuntimeException implements PredictionException -{ - private $exceptions = array(); - private $objectProphecy; - - public function append(PredictionException $exception) - { - $message = $exception->getMessage(); - $message = strtr($message, array("\n" => "\n "))."\n"; - $message = empty($this->exceptions) ? $message : "\n" . $message; - - $this->message = rtrim($this->message.$message); - $this->exceptions[] = $exception; - } - - /** - * @return PredictionException[] - */ - public function getExceptions() - { - return $this->exceptions; - } - - public function setObjectProphecy(ObjectProphecy $objectProphecy) - { - $this->objectProphecy = $objectProphecy; - } - - /** - * @return ObjectProphecy - */ - public function getObjectProphecy() - { - return $this->objectProphecy; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prediction; - -use Prophecy\Prophecy\MethodProphecy; - -class UnexpectedCallsCountException extends UnexpectedCallsException -{ - private $expectedCount; - - public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) - { - parent::__construct($message, $methodProphecy, $calls); - - $this->expectedCount = intval($count); - } - - public function getExpectedCount() - { - return $this->expectedCount; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prediction; - -use Prophecy\Exception\Exception; - -interface PredictionException extends Exception -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prediction; - -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\Prophecy\MethodProphecyException; - -class UnexpectedCallsException extends MethodProphecyException implements PredictionException -{ - private $calls = array(); - - public function __construct($message, MethodProphecy $methodProphecy, array $calls) - { - parent::__construct($message, $methodProphecy); - - $this->calls = $calls; - } - - public function getCalls() - { - return $this->calls; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prediction; - -use Prophecy\Exception\Prophecy\MethodProphecyException; - -class NoCallsException extends MethodProphecyException implements PredictionException -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Prediction; - -use RuntimeException; - -/** - * Basic failed prediction exception. - * Use it for custom prediction failures. - * - * @author Konstantin Kudryashov - */ -class FailedPredictionException extends RuntimeException implements PredictionException -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Exception\Call; - -use Prophecy\Exception\Prophecy\ObjectProphecyException; -use Prophecy\Prophecy\ObjectProphecy; - -class UnexpectedCallException extends ObjectProphecyException -{ - private $methodName; - private $arguments; - - public function __construct($message, ObjectProphecy $objectProphecy, - $methodName, array $arguments) - { - parent::__construct($message, $objectProphecy); - - $this->methodName = $methodName; - $this->arguments = $arguments; - } - - public function getMethodName() - { - return $this->methodName; - } - - public function getArguments() - { - return $this->arguments; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prophecy; - -use SebastianBergmann\Comparator\ComparisonFailure; -use Prophecy\Comparator\Factory as ComparatorFactory; -use Prophecy\Call\Call; -use Prophecy\Doubler\LazyDouble; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Call\CallCenter; -use Prophecy\Exception\Prophecy\ObjectProphecyException; -use Prophecy\Exception\Prophecy\MethodProphecyException; -use Prophecy\Exception\Prediction\AggregateException; -use Prophecy\Exception\Prediction\PredictionException; - -/** - * Object prophecy. - * - * @author Konstantin Kudryashov - */ -class ObjectProphecy implements ProphecyInterface -{ - private $lazyDouble; - private $callCenter; - private $revealer; - private $comparatorFactory; - - /** - * @var MethodProphecy[][] - */ - private $methodProphecies = array(); - - /** - * Initializes object prophecy. - * - * @param LazyDouble $lazyDouble - * @param CallCenter $callCenter - * @param RevealerInterface $revealer - * @param ComparatorFactory $comparatorFactory - */ - public function __construct( - LazyDouble $lazyDouble, - CallCenter $callCenter = null, - RevealerInterface $revealer = null, - ComparatorFactory $comparatorFactory = null - ) { - $this->lazyDouble = $lazyDouble; - $this->callCenter = $callCenter ?: new CallCenter; - $this->revealer = $revealer ?: new Revealer; - - $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); - } - - /** - * Forces double to extend specific class. - * - * @param string $class - * - * @return $this - */ - public function willExtend($class) - { - $this->lazyDouble->setParentClass($class); - - return $this; - } - - /** - * Forces double to implement specific interface. - * - * @param string $interface - * - * @return $this - */ - public function willImplement($interface) - { - $this->lazyDouble->addInterface($interface); - - return $this; - } - - /** - * Sets constructor arguments. - * - * @param array $arguments - * - * @return $this - */ - public function willBeConstructedWith(array $arguments = null) - { - $this->lazyDouble->setArguments($arguments); - - return $this; - } - - /** - * Reveals double. - * - * @return object - * - * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface - */ - public function reveal() - { - $double = $this->lazyDouble->getInstance(); - - if (null === $double || !$double instanceof ProphecySubjectInterface) { - throw new ObjectProphecyException( - "Generated double must implement ProphecySubjectInterface, but it does not.\n". - 'It seems you have wrongly configured doubler without required ClassPatch.', - $this - ); - } - - $double->setProphecy($this); - - return $double; - } - - /** - * Adds method prophecy to object prophecy. - * - * @param MethodProphecy $methodProphecy - * - * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't - * have arguments wildcard - */ - public function addMethodProphecy(MethodProphecy $methodProphecy) - { - $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); - if (null === $argumentsWildcard) { - throw new MethodProphecyException(sprintf( - "Can not add prophecy for a method `%s::%s()`\n". - "as you did not specify arguments wildcard for it.", - get_class($this->reveal()), - $methodProphecy->getMethodName() - ), $methodProphecy); - } - - $methodName = $methodProphecy->getMethodName(); - - if (!isset($this->methodProphecies[$methodName])) { - $this->methodProphecies[$methodName] = array(); - } - - $this->methodProphecies[$methodName][] = $methodProphecy; - } - - /** - * Returns either all or related to single method prophecies. - * - * @param null|string $methodName - * - * @return MethodProphecy[] - */ - public function getMethodProphecies($methodName = null) - { - if (null === $methodName) { - return $this->methodProphecies; - } - - if (!isset($this->methodProphecies[$methodName])) { - return array(); - } - - return $this->methodProphecies[$methodName]; - } - - /** - * Makes specific method call. - * - * @param string $methodName - * @param array $arguments - * - * @return mixed - */ - public function makeProphecyMethodCall($methodName, array $arguments) - { - $arguments = $this->revealer->reveal($arguments); - $return = $this->callCenter->makeCall($this, $methodName, $arguments); - - return $this->revealer->reveal($return); - } - - /** - * Finds calls by method name & arguments wildcard. - * - * @param string $methodName - * @param ArgumentsWildcard $wildcard - * - * @return Call[] - */ - public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) - { - return $this->callCenter->findCalls($methodName, $wildcard); - } - - /** - * Checks that registered method predictions do not fail. - * - * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail - */ - public function checkProphecyMethodsPredictions() - { - $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); - $exception->setObjectProphecy($this); - - foreach ($this->methodProphecies as $prophecies) { - foreach ($prophecies as $prophecy) { - try { - $prophecy->checkPrediction(); - } catch (PredictionException $e) { - $exception->append($e); - } - } - } - - if (count($exception->getExceptions())) { - throw $exception; - } - } - - /** - * Creates new method prophecy using specified method name and arguments. - * - * @param string $methodName - * @param array $arguments - * - * @return MethodProphecy - */ - public function __call($methodName, array $arguments) - { - $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); - - foreach ($this->getMethodProphecies($methodName) as $prophecy) { - $argumentsWildcard = $prophecy->getArgumentsWildcard(); - $comparator = $this->comparatorFactory->getComparatorFor( - $argumentsWildcard, $arguments - ); - - try { - $comparator->assertEquals($argumentsWildcard, $arguments); - return $prophecy; - } catch (ComparisonFailure $failure) {} - } - - return new MethodProphecy($this, $methodName, $arguments); - } - - /** - * Tries to get property value from double. - * - * @param string $name - * - * @return mixed - */ - public function __get($name) - { - return $this->reveal()->$name; - } - - /** - * Tries to set property value to double. - * - * @param string $name - * @param mixed $value - */ - public function __set($name, $value) - { - $this->reveal()->$name = $this->revealer->reveal($value); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prophecy; - -use Prophecy\Argument; -use Prophecy\Prophet; -use Prophecy\Promise; -use Prophecy\Prediction; -use Prophecy\Exception\Doubler\MethodNotFoundException; -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Exception\Prophecy\MethodProphecyException; - -/** - * Method prophecy. - * - * @author Konstantin Kudryashov - */ -class MethodProphecy -{ - private $objectProphecy; - private $methodName; - private $argumentsWildcard; - private $promise; - private $prediction; - private $checkedPredictions = array(); - private $bound = false; - private $voidReturnType = false; - - /** - * Initializes method prophecy. - * - * @param ObjectProphecy $objectProphecy - * @param string $methodName - * @param null|Argument\ArgumentsWildcard|array $arguments - * - * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found - */ - public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null) - { - $double = $objectProphecy->reveal(); - if (!method_exists($double, $methodName)) { - throw new MethodNotFoundException(sprintf( - 'Method `%s::%s()` is not defined.', get_class($double), $methodName - ), get_class($double), $methodName, $arguments); - } - - $this->objectProphecy = $objectProphecy; - $this->methodName = $methodName; - - $reflectedMethod = new \ReflectionMethod($double, $methodName); - if ($reflectedMethod->isFinal()) { - throw new MethodProphecyException(sprintf( - "Can not add prophecy for a method `%s::%s()`\n". - "as it is a final method.", - get_class($double), - $methodName - ), $this); - } - - if (null !== $arguments) { - $this->withArguments($arguments); - } - - if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) { - $type = (string) $reflectedMethod->getReturnType(); - - if ('void' === $type) { - $this->voidReturnType = true; - } - - $this->will(function () use ($type) { - switch ($type) { - case 'void': return; - case 'string': return ''; - case 'float': return 0.0; - case 'int': return 0; - case 'bool': return false; - case 'array': return array(); - - case 'callable': - case 'Closure': - return function () {}; - - case 'Traversable': - case 'Generator': - // Remove eval() when minimum version >=5.5 - /** @var callable $generator */ - $generator = eval('return function () { yield; };'); - return $generator(); - - default: - $prophet = new Prophet; - return $prophet->prophesize($type)->reveal(); - } - }); - } - } - - /** - * Sets argument wildcard. - * - * @param array|Argument\ArgumentsWildcard $arguments - * - * @return $this - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function withArguments($arguments) - { - if (is_array($arguments)) { - $arguments = new Argument\ArgumentsWildcard($arguments); - } - - if (!$arguments instanceof Argument\ArgumentsWildcard) { - throw new InvalidArgumentException(sprintf( - "Either an array or an instance of ArgumentsWildcard expected as\n". - 'a `MethodProphecy::withArguments()` argument, but got %s.', - gettype($arguments) - )); - } - - $this->argumentsWildcard = $arguments; - - return $this; - } - - /** - * Sets custom promise to the prophecy. - * - * @param callable|Promise\PromiseInterface $promise - * - * @return $this - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function will($promise) - { - if (is_callable($promise)) { - $promise = new Promise\CallbackPromise($promise); - } - - if (!$promise instanceof Promise\PromiseInterface) { - throw new InvalidArgumentException(sprintf( - 'Expected callable or instance of PromiseInterface, but got %s.', - gettype($promise) - )); - } - - $this->bindToObjectProphecy(); - $this->promise = $promise; - - return $this; - } - - /** - * Sets return promise to the prophecy. - * - * @see \Prophecy\Promise\ReturnPromise - * - * @return $this - */ - public function willReturn() - { - if ($this->voidReturnType) { - throw new MethodProphecyException( - "The method \"$this->methodName\" has a void return type, and so cannot return anything", - $this - ); - } - - return $this->will(new Promise\ReturnPromise(func_get_args())); - } - - /** - * Sets return argument promise to the prophecy. - * - * @param int $index The zero-indexed number of the argument to return - * - * @see \Prophecy\Promise\ReturnArgumentPromise - * - * @return $this - */ - public function willReturnArgument($index = 0) - { - if ($this->voidReturnType) { - throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this); - } - - return $this->will(new Promise\ReturnArgumentPromise($index)); - } - - /** - * Sets throw promise to the prophecy. - * - * @see \Prophecy\Promise\ThrowPromise - * - * @param string|\Exception $exception Exception class or instance - * - * @return $this - */ - public function willThrow($exception) - { - return $this->will(new Promise\ThrowPromise($exception)); - } - - /** - * Sets custom prediction to the prophecy. - * - * @param callable|Prediction\PredictionInterface $prediction - * - * @return $this - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function should($prediction) - { - if (is_callable($prediction)) { - $prediction = new Prediction\CallbackPrediction($prediction); - } - - if (!$prediction instanceof Prediction\PredictionInterface) { - throw new InvalidArgumentException(sprintf( - 'Expected callable or instance of PredictionInterface, but got %s.', - gettype($prediction) - )); - } - - $this->bindToObjectProphecy(); - $this->prediction = $prediction; - - return $this; - } - - /** - * Sets call prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallPrediction - * - * @return $this - */ - public function shouldBeCalled() - { - return $this->should(new Prediction\CallPrediction); - } - - /** - * Sets no calls prediction to the prophecy. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * - * @return $this - */ - public function shouldNotBeCalled() - { - return $this->should(new Prediction\NoCallsPrediction); - } - - /** - * Sets call times prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @param $count - * - * @return $this - */ - public function shouldBeCalledTimes($count) - { - return $this->should(new Prediction\CallTimesPrediction($count)); - } - - /** - * Sets call times prediction to the prophecy. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @return $this - */ - public function shouldBeCalledOnce() - { - return $this->shouldBeCalledTimes(1); - } - - /** - * Checks provided prediction immediately. - * - * @param callable|Prediction\PredictionInterface $prediction - * - * @return $this - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function shouldHave($prediction) - { - if (is_callable($prediction)) { - $prediction = new Prediction\CallbackPrediction($prediction); - } - - if (!$prediction instanceof Prediction\PredictionInterface) { - throw new InvalidArgumentException(sprintf( - 'Expected callable or instance of PredictionInterface, but got %s.', - gettype($prediction) - )); - } - - if (null === $this->promise && !$this->voidReturnType) { - $this->willReturn(); - } - - $calls = $this->getObjectProphecy()->findProphecyMethodCalls( - $this->getMethodName(), - $this->getArgumentsWildcard() - ); - - try { - $prediction->check($calls, $this->getObjectProphecy(), $this); - $this->checkedPredictions[] = $prediction; - } catch (\Exception $e) { - $this->checkedPredictions[] = $prediction; - - throw $e; - } - - return $this; - } - - /** - * Checks call prediction. - * - * @see \Prophecy\Prediction\CallPrediction - * - * @return $this - */ - public function shouldHaveBeenCalled() - { - return $this->shouldHave(new Prediction\CallPrediction); - } - - /** - * Checks no calls prediction. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * - * @return $this - */ - public function shouldNotHaveBeenCalled() - { - return $this->shouldHave(new Prediction\NoCallsPrediction); - } - - /** - * Checks no calls prediction. - * - * @see \Prophecy\Prediction\NoCallsPrediction - * @deprecated - * - * @return $this - */ - public function shouldNotBeenCalled() - { - return $this->shouldNotHaveBeenCalled(); - } - - /** - * Checks call times prediction. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @param int $count - * - * @return $this - */ - public function shouldHaveBeenCalledTimes($count) - { - return $this->shouldHave(new Prediction\CallTimesPrediction($count)); - } - - /** - * Checks call times prediction. - * - * @see \Prophecy\Prediction\CallTimesPrediction - * - * @return $this - */ - public function shouldHaveBeenCalledOnce() - { - return $this->shouldHaveBeenCalledTimes(1); - } - - /** - * Checks currently registered [with should(...)] prediction. - */ - public function checkPrediction() - { - if (null === $this->prediction) { - return; - } - - $this->shouldHave($this->prediction); - } - - /** - * Returns currently registered promise. - * - * @return null|Promise\PromiseInterface - */ - public function getPromise() - { - return $this->promise; - } - - /** - * Returns currently registered prediction. - * - * @return null|Prediction\PredictionInterface - */ - public function getPrediction() - { - return $this->prediction; - } - - /** - * Returns predictions that were checked on this object. - * - * @return Prediction\PredictionInterface[] - */ - public function getCheckedPredictions() - { - return $this->checkedPredictions; - } - - /** - * Returns object prophecy this method prophecy is tied to. - * - * @return ObjectProphecy - */ - public function getObjectProphecy() - { - return $this->objectProphecy; - } - - /** - * Returns method name. - * - * @return string - */ - public function getMethodName() - { - return $this->methodName; - } - - /** - * Returns arguments wildcard. - * - * @return Argument\ArgumentsWildcard - */ - public function getArgumentsWildcard() - { - return $this->argumentsWildcard; - } - - /** - * @return bool - */ - public function hasReturnVoid() - { - return $this->voidReturnType; - } - - private function bindToObjectProphecy() - { - if ($this->bound) { - return; - } - - $this->getObjectProphecy()->addMethodProphecy($this); - $this->bound = true; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prophecy; - -/** - * Prophecies revealer interface. - * - * @author Konstantin Kudryashov - */ -interface RevealerInterface -{ - /** - * Unwraps value(s). - * - * @param mixed $value - * - * @return mixed - */ - public function reveal($value); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prophecy; - -/** - * Core Prophecy interface. - * - * @author Konstantin Kudryashov - */ -interface ProphecyInterface -{ - /** - * Reveals prophecy object (double) . - * - * @return object - */ - public function reveal(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prophecy; - -/** - * Basic prophecies revealer. - * - * @author Konstantin Kudryashov - */ -class Revealer implements RevealerInterface -{ - /** - * Unwraps value(s). - * - * @param mixed $value - * - * @return mixed - */ - public function reveal($value) - { - if (is_array($value)) { - return array_map(array($this, __FUNCTION__), $value); - } - - if (!is_object($value)) { - return $value; - } - - if ($value instanceof ProphecyInterface) { - $value = $value->reveal(); - } - - return $value; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prophecy; - -/** - * Controllable doubles interface. - * - * @author Konstantin Kudryashov - */ -interface ProphecySubjectInterface -{ - /** - * Sets subject prophecy. - * - * @param ProphecyInterface $prophecy - */ - public function setProphecy(ProphecyInterface $prophecy); - - /** - * Returns subject prophecy. - * - * @return ProphecyInterface - */ - public function getProphecy(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument; - -/** - * Arguments wildcarding. - * - * @author Konstantin Kudryashov - */ -class ArgumentsWildcard -{ - /** - * @var Token\TokenInterface[] - */ - private $tokens = array(); - private $string; - - /** - * Initializes wildcard. - * - * @param array $arguments Array of argument tokens or values - */ - public function __construct(array $arguments) - { - foreach ($arguments as $argument) { - if (!$argument instanceof Token\TokenInterface) { - $argument = new Token\ExactValueToken($argument); - } - - $this->tokens[] = $argument; - } - } - - /** - * Calculates wildcard match score for provided arguments. - * - * @param array $arguments - * - * @return false|int False OR integer score (higher - better) - */ - public function scoreArguments(array $arguments) - { - if (0 == count($arguments) && 0 == count($this->tokens)) { - return 1; - } - - $arguments = array_values($arguments); - $totalScore = 0; - foreach ($this->tokens as $i => $token) { - $argument = isset($arguments[$i]) ? $arguments[$i] : null; - if (1 >= $score = $token->scoreArgument($argument)) { - return false; - } - - $totalScore += $score; - - if (true === $token->isLast()) { - return $totalScore; - } - } - - if (count($arguments) > count($this->tokens)) { - return false; - } - - return $totalScore; - } - - /** - * Returns string representation for wildcard. - * - * @return string - */ - public function __toString() - { - if (null === $this->string) { - $this->string = implode(', ', array_map(function ($token) { - return (string) $token; - }, $this->tokens)); - } - - return $this->string; - } - - /** - * @return array - */ - public function getTokens() - { - return $this->tokens; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -use Prophecy\Exception\InvalidArgumentException; - -/** - * Callback-verified token. - * - * @author Konstantin Kudryashov - */ -class CallbackToken implements TokenInterface -{ - private $callback; - - /** - * Initializes token. - * - * @param callable $callback - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException(sprintf( - 'Callable expected as an argument to CallbackToken, but got %s.', - gettype($callback) - )); - } - - $this->callback = $callback; - } - - /** - * Scores 7 if callback returns true, false otherwise. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - return call_user_func($this->callback, $argument) ? 7 : false; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return 'callback()'; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Logical AND token. - * - * @author Boris Mikhaylov - */ -class LogicalAndToken implements TokenInterface -{ - private $tokens = array(); - - /** - * @param array $arguments exact values or tokens - */ - public function __construct(array $arguments) - { - foreach ($arguments as $argument) { - if (!$argument instanceof TokenInterface) { - $argument = new ExactValueToken($argument); - } - $this->tokens[] = $argument; - } - } - - /** - * Scores maximum score from scores returned by tokens for this argument if all of them score. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - if (0 === count($this->tokens)) { - return false; - } - - $maxScore = 0; - foreach ($this->tokens as $token) { - $score = $token->scoreArgument($argument); - if (false === $score) { - return false; - } - $maxScore = max($score, $maxScore); - } - - return $maxScore; - } - - /** - * Returns false. - * - * @return boolean - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('bool(%s)', implode(' AND ', $this->tokens)); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Logical NOT token. - * - * @author Boris Mikhaylov - */ -class LogicalNotToken implements TokenInterface -{ - /** @var \Prophecy\Argument\Token\TokenInterface */ - private $token; - - /** - * @param mixed $value exact value or token - */ - public function __construct($value) - { - $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value); - } - - /** - * Scores 4 when preset token does not match the argument. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - return false === $this->token->scoreArgument($argument) ? 4 : false; - } - - /** - * Returns true if preset token is last. - * - * @return bool|int - */ - public function isLast() - { - return $this->token->isLast(); - } - - /** - * Returns originating token. - * - * @return TokenInterface - */ - public function getOriginatingToken() - { - return $this->token; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('not(%s)', $this->token); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Array every entry token. - * - * @author Adrien Brault - */ -class ArrayEveryEntryToken implements TokenInterface -{ - /** - * @var TokenInterface - */ - private $value; - - /** - * @param mixed $value exact value or token - */ - public function __construct($value) - { - if (!$value instanceof TokenInterface) { - $value = new ExactValueToken($value); - } - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function scoreArgument($argument) - { - if (!$argument instanceof \Traversable && !is_array($argument)) { - return false; - } - - $scores = array(); - foreach ($argument as $key => $argumentEntry) { - $scores[] = $this->value->scoreArgument($argumentEntry); - } - - if (empty($scores) || in_array(false, $scores, true)) { - return false; - } - - return array_sum($scores) / count($scores); - } - - /** - * {@inheritdoc} - */ - public function isLast() - { - return false; - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return sprintf('[%s, ..., %s]', $this->value, $this->value); - } - - /** - * @return TokenInterface - */ - public function getValue() - { - return $this->value; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -use SebastianBergmann\Comparator\ComparisonFailure; -use Prophecy\Comparator\Factory as ComparatorFactory; -use Prophecy\Util\StringUtil; - -/** - * Exact value token. - * - * @author Konstantin Kudryashov - */ -class ExactValueToken implements TokenInterface -{ - private $value; - private $string; - private $util; - private $comparatorFactory; - - /** - * Initializes token. - * - * @param mixed $value - * @param StringUtil $util - * @param ComparatorFactory $comparatorFactory - */ - public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) - { - $this->value = $value; - $this->util = $util ?: new StringUtil(); - - $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); - } - - /** - * Scores 10 if argument matches preset value. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - if (is_object($argument) && is_object($this->value)) { - $comparator = $this->comparatorFactory->getComparatorFor( - $argument, $this->value - ); - - try { - $comparator->assertEquals($argument, $this->value); - return 10; - } catch (ComparisonFailure $failure) {} - } - - // If either one is an object it should be castable to a string - if (is_object($argument) xor is_object($this->value)) { - if (is_object($argument) && !method_exists($argument, '__toString')) { - return false; - } - - if (is_object($this->value) && !method_exists($this->value, '__toString')) { - return false; - } - } elseif (is_numeric($argument) && is_numeric($this->value)) { - // noop - } elseif (gettype($argument) !== gettype($this->value)) { - return false; - } - - return $argument == $this->value ? 10 : false; - } - - /** - * Returns preset value against which token checks arguments. - * - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - if (null === $this->string) { - $this->string = sprintf('exact(%s)', $this->util->stringify($this->value)); - } - - return $this->string; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -use Prophecy\Exception\InvalidArgumentException; - -/** - * Value type token. - * - * @author Konstantin Kudryashov - */ -class TypeToken implements TokenInterface -{ - private $type; - - /** - * @param string $type - */ - public function __construct($type) - { - $checker = "is_{$type}"; - if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) { - throw new InvalidArgumentException(sprintf( - 'Type or class name expected as an argument to TypeToken, but got %s.', $type - )); - } - - $this->type = $type; - } - - /** - * Scores 5 if argument has the same type this token was constructed with. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - $checker = "is_{$this->type}"; - if (function_exists($checker)) { - return call_user_func($checker, $argument) ? 5 : false; - } - - return $argument instanceof $this->type ? 5 : false; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('type(%s)', $this->type); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Approximate value token - * - * @author Daniel Leech - */ -class ApproximateValueToken implements TokenInterface -{ - private $value; - private $precision; - - public function __construct($value, $precision = 0) - { - $this->value = $value; - $this->precision = $precision; - } - - /** - * {@inheritdoc} - */ - public function scoreArgument($argument) - { - return round($argument, $this->precision) === round($this->value, $this->precision) ? 10 : false; - } - - /** - * {@inheritdoc} - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('≅%s', round($this->value, $this->precision)); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Any values token. - * - * @author Konstantin Kudryashov - */ -class AnyValuesToken implements TokenInterface -{ - /** - * Always scores 2 for any argument. - * - * @param $argument - * - * @return int - */ - public function scoreArgument($argument) - { - return 2; - } - - /** - * Returns true to stop wildcard from processing other tokens. - * - * @return bool - */ - public function isLast() - { - return true; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return '* [, ...]'; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -use Prophecy\Util\StringUtil; - -/** - * Identical value token. - * - * @author Florian Voutzinos - */ -class IdenticalValueToken implements TokenInterface -{ - private $value; - private $string; - private $util; - - /** - * Initializes token. - * - * @param mixed $value - * @param StringUtil $util - */ - public function __construct($value, StringUtil $util = null) - { - $this->value = $value; - $this->util = $util ?: new StringUtil(); - } - - /** - * Scores 11 if argument matches preset value. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - return $argument === $this->value ? 11 : false; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - if (null === $this->string) { - $this->string = sprintf('identical(%s)', $this->util->stringify($this->value)); - } - - return $this->string; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Array elements count token. - * - * @author Boris Mikhaylov - */ - -class ArrayCountToken implements TokenInterface -{ - private $count; - - /** - * @param integer $value - */ - public function __construct($value) - { - $this->count = $value; - } - - /** - * Scores 6 when argument has preset number of elements. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false; - } - - /** - * Returns false. - * - * @return boolean - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('count(%s)', $this->count); - } - - /** - * Returns true if object is either array or instance of \Countable - * - * @param $argument - * @return bool - */ - private function isCountable($argument) - { - return (is_array($argument) || $argument instanceof \Countable); - } - - /** - * Returns true if $argument has expected number of elements - * - * @param array|\Countable $argument - * - * @return bool - */ - private function hasProperCount($argument) - { - return $this->count === count($argument); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -use Prophecy\Exception\InvalidArgumentException; - -/** - * Array entry token. - * - * @author Boris Mikhaylov - */ -class ArrayEntryToken implements TokenInterface -{ - /** @var \Prophecy\Argument\Token\TokenInterface */ - private $key; - /** @var \Prophecy\Argument\Token\TokenInterface */ - private $value; - - /** - * @param mixed $key exact value or token - * @param mixed $value exact value or token - */ - public function __construct($key, $value) - { - $this->key = $this->wrapIntoExactValueToken($key); - $this->value = $this->wrapIntoExactValueToken($value); - } - - /** - * Scores half of combined scores from key and value tokens for same entry. Capped at 8. - * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. - * - * @param array|\ArrayAccess|\Traversable $argument - * - * @throws \Prophecy\Exception\InvalidArgumentException - * @return bool|int - */ - public function scoreArgument($argument) - { - if ($argument instanceof \Traversable) { - $argument = iterator_to_array($argument); - } - - if ($argument instanceof \ArrayAccess) { - $argument = $this->convertArrayAccessToEntry($argument); - } - - if (!is_array($argument) || empty($argument)) { - return false; - } - - $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument)); - $valueScores = array_map(array($this->value,'scoreArgument'), $argument); - $scoreEntry = function ($value, $key) { - return $value && $key ? min(8, ($key + $value) / 2) : false; - }; - - return max(array_map($scoreEntry, $valueScores, $keyScores)); - } - - /** - * Returns false. - * - * @return boolean - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('[..., %s => %s, ...]', $this->key, $this->value); - } - - /** - * Returns key - * - * @return TokenInterface - */ - public function getKey() - { - return $this->key; - } - - /** - * Returns value - * - * @return TokenInterface - */ - public function getValue() - { - return $this->value; - } - - /** - * Wraps non token $value into ExactValueToken - * - * @param $value - * @return TokenInterface - */ - private function wrapIntoExactValueToken($value) - { - return $value instanceof TokenInterface ? $value : new ExactValueToken($value); - } - - /** - * Converts instance of \ArrayAccess to key => value array entry - * - * @param \ArrayAccess $object - * - * @return array|null - * @throws \Prophecy\Exception\InvalidArgumentException - */ - private function convertArrayAccessToEntry(\ArrayAccess $object) - { - if (!$this->key instanceof ExactValueToken) { - throw new InvalidArgumentException(sprintf( - 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL. - 'But you used `%s`.', - $this->key - )); - } - - $key = $this->key->getValue(); - - return $object->offsetExists($key) ? array($key => $object[$key]) : array(); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Argument token interface. - * - * @author Konstantin Kudryashov - */ -interface TokenInterface -{ - /** - * Calculates token match score for provided argument. - * - * @param $argument - * - * @return bool|int - */ - public function scoreArgument($argument); - - /** - * Returns true if this token prevents check of other tokens (is last one). - * - * @return bool|int - */ - public function isLast(); - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * String contains token. - * - * @author Peter Mitchell - */ -class StringContainsToken implements TokenInterface -{ - private $value; - - /** - * Initializes token. - * - * @param string $value - */ - public function __construct($value) - { - $this->value = $value; - } - - public function scoreArgument($argument) - { - return is_string($argument) && strpos($argument, $this->value) !== false ? 6 : false; - } - - /** - * Returns preset value against which token checks arguments. - * - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('contains("%s")', $this->value); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -/** - * Any single value token. - * - * @author Konstantin Kudryashov - */ -class AnyValueToken implements TokenInterface -{ - /** - * Always scores 3 for any argument. - * - * @param $argument - * - * @return int - */ - public function scoreArgument($argument) - { - return 3; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return '*'; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Argument\Token; - -use SebastianBergmann\Comparator\ComparisonFailure; -use Prophecy\Comparator\Factory as ComparatorFactory; -use Prophecy\Util\StringUtil; - -/** - * Object state-checker token. - * - * @author Konstantin Kudryashov - */ -class ObjectStateToken implements TokenInterface -{ - private $name; - private $value; - private $util; - private $comparatorFactory; - - /** - * Initializes token. - * - * @param string $methodName - * @param mixed $value Expected return value - * @param null|StringUtil $util - * @param ComparatorFactory $comparatorFactory - */ - public function __construct( - $methodName, - $value, - StringUtil $util = null, - ComparatorFactory $comparatorFactory = null - ) { - $this->name = $methodName; - $this->value = $value; - $this->util = $util ?: new StringUtil; - - $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); - } - - /** - * Scores 8 if argument is an object, which method returns expected value. - * - * @param mixed $argument - * - * @return bool|int - */ - public function scoreArgument($argument) - { - if (is_object($argument) && method_exists($argument, $this->name)) { - $actual = call_user_func(array($argument, $this->name)); - - $comparator = $this->comparatorFactory->getComparatorFor( - $this->value, $actual - ); - - try { - $comparator->assertEquals($this->value, $actual); - return 8; - } catch (ComparisonFailure $failure) { - return false; - } - } - - if (is_object($argument) && property_exists($argument, $this->name)) { - return $argument->{$this->name} === $this->value ? 8 : false; - } - - return false; - } - - /** - * Returns false. - * - * @return bool - */ - public function isLast() - { - return false; - } - - /** - * Returns string representation for token. - * - * @return string - */ - public function __toString() - { - return sprintf('state(%s(), %s)', - $this->name, - $this->util->stringify($this->value) - ); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator; - -/** - * Reflection interface. - * All reflected classes implement this interface. - * - * @author Konstantin Kudryashov - */ -interface ReflectionInterface -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator; - -use Prophecy\Exception\Doubler\ClassCreatorException; - -/** - * Class creator. - * Creates specific class in current environment. - * - * @author Konstantin Kudryashov - */ -class ClassCreator -{ - private $generator; - - /** - * Initializes creator. - * - * @param ClassCodeGenerator $generator - */ - public function __construct(ClassCodeGenerator $generator = null) - { - $this->generator = $generator ?: new ClassCodeGenerator; - } - - /** - * Creates class. - * - * @param string $classname - * @param Node\ClassNode $class - * - * @return mixed - * - * @throws \Prophecy\Exception\Doubler\ClassCreatorException - */ - public function create($classname, Node\ClassNode $class) - { - $code = $this->generator->generate($classname, $class); - $return = eval($code); - - if (!class_exists($classname, false)) { - if (count($class->getInterfaces())) { - throw new ClassCreatorException(sprintf( - 'Could not double `%s` and implement interfaces: [%s].', - $class->getParentClass(), implode(', ', $class->getInterfaces()) - ), $class); - } - - throw new ClassCreatorException( - sprintf('Could not double `%s`.', $class->getParentClass()), - $class - ); - } - - return $return; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator\Node; - -use Prophecy\Doubler\Generator\TypeHintReference; -use Prophecy\Exception\InvalidArgumentException; - -/** - * Method node. - * - * @author Konstantin Kudryashov - */ -class MethodNode -{ - private $name; - private $code; - private $visibility = 'public'; - private $static = false; - private $returnsReference = false; - private $returnType; - private $nullableReturnType = false; - - /** - * @var ArgumentNode[] - */ - private $arguments = array(); - - /** - * @var TypeHintReference - */ - private $typeHintReference; - - /** - * @param string $name - * @param string $code - */ - public function __construct($name, $code = null, TypeHintReference $typeHintReference = null) - { - $this->name = $name; - $this->code = $code; - $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); - } - - public function getVisibility() - { - return $this->visibility; - } - - /** - * @param string $visibility - */ - public function setVisibility($visibility) - { - $visibility = strtolower($visibility); - - if (!in_array($visibility, array('public', 'private', 'protected'))) { - throw new InvalidArgumentException(sprintf( - '`%s` method visibility is not supported.', $visibility - )); - } - - $this->visibility = $visibility; - } - - public function isStatic() - { - return $this->static; - } - - public function setStatic($static = true) - { - $this->static = (bool) $static; - } - - public function returnsReference() - { - return $this->returnsReference; - } - - public function setReturnsReference() - { - $this->returnsReference = true; - } - - public function getName() - { - return $this->name; - } - - public function addArgument(ArgumentNode $argument) - { - $this->arguments[] = $argument; - } - - /** - * @return ArgumentNode[] - */ - public function getArguments() - { - return $this->arguments; - } - - public function hasReturnType() - { - return null !== $this->returnType; - } - - /** - * @param string $type - */ - public function setReturnType($type = null) - { - if ($type === '' || $type === null) { - $this->returnType = null; - return; - } - $typeMap = array( - 'double' => 'float', - 'real' => 'float', - 'boolean' => 'bool', - 'integer' => 'int', - ); - if (isset($typeMap[$type])) { - $type = $typeMap[$type]; - } - $this->returnType = $this->typeHintReference->isBuiltInReturnTypeHint($type) ? - $type : - '\\' . ltrim($type, '\\'); - } - - public function getReturnType() - { - return $this->returnType; - } - - /** - * @param bool $bool - */ - public function setNullableReturnType($bool = true) - { - $this->nullableReturnType = (bool) $bool; - } - - /** - * @return bool - */ - public function hasNullableReturnType() - { - return $this->nullableReturnType; - } - - /** - * @param string $code - */ - public function setCode($code) - { - $this->code = $code; - } - - public function getCode() - { - if ($this->returnsReference) - { - return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; - } - - return (string) $this->code; - } - - public function useParentCode() - { - $this->code = sprintf( - 'return parent::%s(%s);', $this->getName(), implode(', ', - array_map(array($this, 'generateArgument'), $this->arguments) - ) - ); - } - - private function generateArgument(ArgumentNode $arg) - { - $argument = '$'.$arg->getName(); - - if ($arg->isVariadic()) { - $argument = '...'.$argument; - } - - return $argument; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator\Node; - -use Prophecy\Exception\Doubler\MethodNotExtendableException; -use Prophecy\Exception\InvalidArgumentException; - -/** - * Class node. - * - * @author Konstantin Kudryashov - */ -class ClassNode -{ - private $parentClass = 'stdClass'; - private $interfaces = array(); - private $properties = array(); - private $unextendableMethods = array(); - - /** - * @var MethodNode[] - */ - private $methods = array(); - - public function getParentClass() - { - return $this->parentClass; - } - - /** - * @param string $class - */ - public function setParentClass($class) - { - $this->parentClass = $class ?: 'stdClass'; - } - - /** - * @return string[] - */ - public function getInterfaces() - { - return $this->interfaces; - } - - /** - * @param string $interface - */ - public function addInterface($interface) - { - if ($this->hasInterface($interface)) { - return; - } - - array_unshift($this->interfaces, $interface); - } - - /** - * @param string $interface - * - * @return bool - */ - public function hasInterface($interface) - { - return in_array($interface, $this->interfaces); - } - - public function getProperties() - { - return $this->properties; - } - - public function addProperty($name, $visibility = 'public') - { - $visibility = strtolower($visibility); - - if (!in_array($visibility, array('public', 'private', 'protected'))) { - throw new InvalidArgumentException(sprintf( - '`%s` property visibility is not supported.', $visibility - )); - } - - $this->properties[$name] = $visibility; - } - - /** - * @return MethodNode[] - */ - public function getMethods() - { - return $this->methods; - } - - public function addMethod(MethodNode $method, $force = false) - { - if (!$this->isExtendable($method->getName())){ - $message = sprintf( - 'Method `%s` is not extendable, so can not be added.', $method->getName() - ); - throw new MethodNotExtendableException($message, $this->getParentClass(), $method->getName()); - } - - if ($force || !isset($this->methods[$method->getName()])) { - $this->methods[$method->getName()] = $method; - } - } - - public function removeMethod($name) - { - unset($this->methods[$name]); - } - - /** - * @param string $name - * - * @return MethodNode|null - */ - public function getMethod($name) - { - return $this->hasMethod($name) ? $this->methods[$name] : null; - } - - /** - * @param string $name - * - * @return bool - */ - public function hasMethod($name) - { - return isset($this->methods[$name]); - } - - /** - * @return string[] - */ - public function getUnextendableMethods() - { - return $this->unextendableMethods; - } - - /** - * @param string $unextendableMethod - */ - public function addUnextendableMethod($unextendableMethod) - { - if (!$this->isExtendable($unextendableMethod)){ - return; - } - $this->unextendableMethods[] = $unextendableMethod; - } - - /** - * @param string $method - * @return bool - */ - public function isExtendable($method) - { - return !in_array($method, $this->unextendableMethods); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator\Node; - -/** - * Argument node. - * - * @author Konstantin Kudryashov - */ -class ArgumentNode -{ - private $name; - private $typeHint; - private $default; - private $optional = false; - private $byReference = false; - private $isVariadic = false; - private $isNullable = false; - - /** - * @param string $name - */ - public function __construct($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - - public function getTypeHint() - { - return $this->typeHint; - } - - public function setTypeHint($typeHint = null) - { - $this->typeHint = $typeHint; - } - - public function hasDefault() - { - return $this->isOptional() && !$this->isVariadic(); - } - - public function getDefault() - { - return $this->default; - } - - public function setDefault($default = null) - { - $this->optional = true; - $this->default = $default; - } - - public function isOptional() - { - return $this->optional; - } - - public function setAsPassedByReference($byReference = true) - { - $this->byReference = $byReference; - } - - public function isPassedByReference() - { - return $this->byReference; - } - - public function setAsVariadic($isVariadic = true) - { - $this->isVariadic = $isVariadic; - } - - public function isVariadic() - { - return $this->isVariadic; - } - - public function isNullable() - { - return $this->isNullable; - } - - public function setAsNullable($isNullable = true) - { - $this->isNullable = $isNullable; - } -} -= 50400; - - case 'bool': - case 'float': - case 'int': - case 'string': - return PHP_VERSION_ID >= 70000; - - case 'iterable': - return PHP_VERSION_ID >= 70100; - - case 'object': - return PHP_VERSION_ID >= 70200; - - default: - return false; - } - } - - public function isBuiltInReturnTypeHint($type) - { - if ($type === 'void') { - return PHP_VERSION_ID >= 70100; - } - - return $this->isBuiltInParamTypeHint($type); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator; - -/** - * Class code creator. - * Generates PHP code for specific class node tree. - * - * @author Konstantin Kudryashov - */ -class ClassCodeGenerator -{ - /** - * @var TypeHintReference - */ - private $typeHintReference; - - public function __construct(TypeHintReference $typeHintReference = null) - { - $this->typeHintReference = $typeHintReference ?: new TypeHintReference(); - } - - /** - * Generates PHP code for class node. - * - * @param string $classname - * @param Node\ClassNode $class - * - * @return string - */ - public function generate($classname, Node\ClassNode $class) - { - $parts = explode('\\', $classname); - $classname = array_pop($parts); - $namespace = implode('\\', $parts); - - $code = sprintf("class %s extends \%s implements %s {\n", - $classname, $class->getParentClass(), implode(', ', - array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) - ) - ); - - foreach ($class->getProperties() as $name => $visibility) { - $code .= sprintf("%s \$%s;\n", $visibility, $name); - } - $code .= "\n"; - - foreach ($class->getMethods() as $method) { - $code .= $this->generateMethod($method)."\n"; - } - $code .= "\n}"; - - return sprintf("namespace %s {\n%s\n}", $namespace, $code); - } - - private function generateMethod(Node\MethodNode $method) - { - $php = sprintf("%s %s function %s%s(%s)%s {\n", - $method->getVisibility(), - $method->isStatic() ? 'static' : '', - $method->returnsReference() ? '&':'', - $method->getName(), - implode(', ', $this->generateArguments($method->getArguments())), - $this->getReturnType($method) - ); - $php .= $method->getCode()."\n"; - - return $php.'}'; - } - - /** - * @return string - */ - private function getReturnType(Node\MethodNode $method) - { - if (version_compare(PHP_VERSION, '7.1', '>=')) { - if ($method->hasReturnType()) { - return $method->hasNullableReturnType() - ? sprintf(': ?%s', $method->getReturnType()) - : sprintf(': %s', $method->getReturnType()); - } - } - - if (version_compare(PHP_VERSION, '7.0', '>=')) { - return $method->hasReturnType() && $method->getReturnType() !== 'void' - ? sprintf(': %s', $method->getReturnType()) - : ''; - } - - return ''; - } - - private function generateArguments(array $arguments) - { - $typeHintReference = $this->typeHintReference; - return array_map(function (Node\ArgumentNode $argument) use ($typeHintReference) { - $php = ''; - - if (version_compare(PHP_VERSION, '7.1', '>=')) { - $php .= $argument->isNullable() ? '?' : ''; - } - - if ($hint = $argument->getTypeHint()) { - $php .= $typeHintReference->isBuiltInParamTypeHint($hint) ? $hint : '\\'.$hint; - } - - $php .= ' '.($argument->isPassedByReference() ? '&' : ''); - - $php .= $argument->isVariadic() ? '...' : ''; - - $php .= '$'.$argument->getName(); - - if ($argument->isOptional() && !$argument->isVariadic()) { - $php .= ' = '.var_export($argument->getDefault(), true); - } - - return $php; - }, $arguments); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\Generator; - -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Exception\Doubler\ClassMirrorException; -use ReflectionClass; -use ReflectionMethod; -use ReflectionParameter; - -/** - * Class mirror. - * Core doubler class. Mirrors specific class and/or interfaces into class node tree. - * - * @author Konstantin Kudryashov - */ -class ClassMirror -{ - private static $reflectableMethods = array( - '__construct', - '__destruct', - '__sleep', - '__wakeup', - '__toString', - '__call', - '__invoke' - ); - - /** - * Reflects provided arguments into class node. - * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces - * - * @return Node\ClassNode - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function reflect(ReflectionClass $class = null, array $interfaces) - { - $node = new Node\ClassNode; - - if (null !== $class) { - if (true === $class->isInterface()) { - throw new InvalidArgumentException(sprintf( - "Could not reflect %s as a class, because it\n". - "is interface - use the second argument instead.", - $class->getName() - )); - } - - $this->reflectClassToNode($class, $node); - } - - foreach ($interfaces as $interface) { - if (!$interface instanceof ReflectionClass) { - throw new InvalidArgumentException(sprintf( - "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". - "a second argument to `ClassMirror::reflect(...)`, but got %s.", - is_object($interface) ? get_class($interface).' class' : gettype($interface) - )); - } - if (false === $interface->isInterface()) { - throw new InvalidArgumentException(sprintf( - "Could not reflect %s as an interface, because it\n". - "is class - use the first argument instead.", - $interface->getName() - )); - } - - $this->reflectInterfaceToNode($interface, $node); - } - - $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface'); - - return $node; - } - - private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node) - { - if (true === $class->isFinal()) { - throw new ClassMirrorException(sprintf( - 'Could not reflect class %s as it is marked final.', $class->getName() - ), $class); - } - - $node->setParentClass($class->getName()); - - foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { - if (false === $method->isProtected()) { - continue; - } - - $this->reflectMethodToNode($method, $node); - } - - foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - if (0 === strpos($method->getName(), '_') - && !in_array($method->getName(), self::$reflectableMethods)) { - continue; - } - - if (true === $method->isFinal()) { - $node->addUnextendableMethod($method->getName()); - continue; - } - - $this->reflectMethodToNode($method, $node); - } - } - - private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node) - { - $node->addInterface($interface->getName()); - - foreach ($interface->getMethods() as $method) { - $this->reflectMethodToNode($method, $node); - } - } - - private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode) - { - $node = new Node\MethodNode($method->getName()); - - if (true === $method->isProtected()) { - $node->setVisibility('protected'); - } - - if (true === $method->isStatic()) { - $node->setStatic(); - } - - if (true === $method->returnsReference()) { - $node->setReturnsReference(); - } - - if (version_compare(PHP_VERSION, '7.0', '>=') && $method->hasReturnType()) { - $returnType = (string) $method->getReturnType(); - $returnTypeLower = strtolower($returnType); - - if ('self' === $returnTypeLower) { - $returnType = $method->getDeclaringClass()->getName(); - } - if ('parent' === $returnTypeLower) { - $returnType = $method->getDeclaringClass()->getParentClass()->getName(); - } - - $node->setReturnType($returnType); - - if (version_compare(PHP_VERSION, '7.1', '>=') && $method->getReturnType()->allowsNull()) { - $node->setNullableReturnType(true); - } - } - - if (is_array($params = $method->getParameters()) && count($params)) { - foreach ($params as $param) { - $this->reflectArgumentToNode($param, $node); - } - } - - $classNode->addMethod($node); - } - - private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode) - { - $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); - $node = new Node\ArgumentNode($name); - - $node->setTypeHint($this->getTypeHint($parameter)); - - if ($this->isVariadic($parameter)) { - $node->setAsVariadic(); - } - - if ($this->hasDefaultValue($parameter)) { - $node->setDefault($this->getDefaultValue($parameter)); - } - - if ($parameter->isPassedByReference()) { - $node->setAsPassedByReference(); - } - - $node->setAsNullable($this->isNullable($parameter)); - - $methodNode->addArgument($node); - } - - private function hasDefaultValue(ReflectionParameter $parameter) - { - if ($this->isVariadic($parameter)) { - return false; - } - - if ($parameter->isDefaultValueAvailable()) { - return true; - } - - return $parameter->isOptional() || $this->isNullable($parameter); - } - - private function getDefaultValue(ReflectionParameter $parameter) - { - if (!$parameter->isDefaultValueAvailable()) { - return null; - } - - return $parameter->getDefaultValue(); - } - - private function getTypeHint(ReflectionParameter $parameter) - { - if (null !== $className = $this->getParameterClassName($parameter)) { - return $className; - } - - if (true === $parameter->isArray()) { - return 'array'; - } - - if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) { - return 'callable'; - } - - if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) { - return (string) $parameter->getType(); - } - - return null; - } - - private function isVariadic(ReflectionParameter $parameter) - { - return PHP_VERSION_ID >= 50600 && $parameter->isVariadic(); - } - - private function isNullable(ReflectionParameter $parameter) - { - return $parameter->allowsNull() && null !== $this->getTypeHint($parameter); - } - - private function getParameterClassName(ReflectionParameter $parameter) - { - try { - return $parameter->getClass() ? $parameter->getClass()->getName() : null; - } catch (\ReflectionException $e) { - preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches); - - return isset($matches[1]) ? $matches[1] : null; - } - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler; - -use Doctrine\Instantiator\Instantiator; -use Prophecy\Doubler\ClassPatch\ClassPatchInterface; -use Prophecy\Doubler\Generator\ClassMirror; -use Prophecy\Doubler\Generator\ClassCreator; -use Prophecy\Exception\InvalidArgumentException; -use ReflectionClass; - -/** - * Cached class doubler. - * Prevents mirroring/creation of the same structure twice. - * - * @author Konstantin Kudryashov - */ -class Doubler -{ - private $mirror; - private $creator; - private $namer; - - /** - * @var ClassPatchInterface[] - */ - private $patches = array(); - - /** - * @var \Doctrine\Instantiator\Instantiator - */ - private $instantiator; - - /** - * Initializes doubler. - * - * @param ClassMirror $mirror - * @param ClassCreator $creator - * @param NameGenerator $namer - */ - public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, - NameGenerator $namer = null) - { - $this->mirror = $mirror ?: new ClassMirror; - $this->creator = $creator ?: new ClassCreator; - $this->namer = $namer ?: new NameGenerator; - } - - /** - * Returns list of registered class patches. - * - * @return ClassPatchInterface[] - */ - public function getClassPatches() - { - return $this->patches; - } - - /** - * Registers new class patch. - * - * @param ClassPatchInterface $patch - */ - public function registerClassPatch(ClassPatchInterface $patch) - { - $this->patches[] = $patch; - - @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { - return $patch2->getPriority() - $patch1->getPriority(); - }); - } - - /** - * Creates double from specific class or/and list of interfaces. - * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces Array of ReflectionClass instances - * @param array $args Constructor arguments - * - * @return DoubleInterface - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function double(ReflectionClass $class = null, array $interfaces, array $args = null) - { - foreach ($interfaces as $interface) { - if (!$interface instanceof ReflectionClass) { - throw new InvalidArgumentException(sprintf( - "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". - "a second argument to `Doubler::double(...)`, but got %s.", - is_object($interface) ? get_class($interface).' class' : gettype($interface) - )); - } - } - - $classname = $this->createDoubleClass($class, $interfaces); - $reflection = new ReflectionClass($classname); - - if (null !== $args) { - return $reflection->newInstanceArgs($args); - } - if ((null === $constructor = $reflection->getConstructor()) - || ($constructor->isPublic() && !$constructor->isFinal())) { - return $reflection->newInstance(); - } - - if (!$this->instantiator) { - $this->instantiator = new Instantiator(); - } - - return $this->instantiator->instantiate($classname); - } - - /** - * Creates double class and returns its FQN. - * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces - * - * @return string - */ - protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) - { - $name = $this->namer->name($class, $interfaces); - $node = $this->mirror->reflect($class, $interfaces); - - foreach ($this->patches as $patch) { - if ($patch->supports($node)) { - $patch->apply($node); - } - } - - $this->creator->create($name, $node); - - return $name; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler; - -use ReflectionClass; - -/** - * Cached class doubler. - * Prevents mirroring/creation of the same structure twice. - * - * @author Konstantin Kudryashov - */ -class CachedDoubler extends Doubler -{ - private $classes = array(); - - /** - * {@inheritdoc} - */ - public function registerClassPatch(ClassPatch\ClassPatchInterface $patch) - { - $this->classes[] = array(); - - parent::registerClassPatch($patch); - } - - /** - * {@inheritdoc} - */ - protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) - { - $classId = $this->generateClassId($class, $interfaces); - if (isset($this->classes[$classId])) { - return $this->classes[$classId]; - } - - return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces); - } - - /** - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces - * - * @return string - */ - private function generateClassId(ReflectionClass $class = null, array $interfaces) - { - $parts = array(); - if (null !== $class) { - $parts[] = $class->getName(); - } - foreach ($interfaces as $interface) { - $parts[] = $interface->getName(); - } - sort($parts); - - return md5(implode('', $parts)); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler; - -use ReflectionClass; - -/** - * Name generator. - * Generates classname for double. - * - * @author Konstantin Kudryashov - */ -class NameGenerator -{ - private static $counter = 1; - - /** - * Generates name. - * - * @param ReflectionClass $class - * @param ReflectionClass[] $interfaces - * - * @return string - */ - public function name(ReflectionClass $class = null, array $interfaces) - { - $parts = array(); - - if (null !== $class) { - $parts[] = $class->getName(); - } else { - foreach ($interfaces as $interface) { - $parts[] = $interface->getShortName(); - } - } - - if (!count($parts)) { - $parts[] = 'stdClass'; - } - - return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler; - -/** - * Core double interface. - * All doubled classes will implement this one. - * - * @author Konstantin Kudryashov - */ -interface DoubleInterface -{ -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; - -/** - * Disable constructor. - * Makes all constructor arguments optional. - * - * @author Konstantin Kudryashov - */ -class DisableConstructorPatch implements ClassPatchInterface -{ - /** - * Checks if class has `__construct` method. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - return true; - } - - /** - * Makes all class constructor arguments optional. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - if (!$node->hasMethod('__construct')) { - $node->addMethod(new MethodNode('__construct', '')); - - return; - } - - $constructor = $node->getMethod('__construct'); - foreach ($constructor->getArguments() as $argument) { - $argument->setDefault(null); - } - - $constructor->setCode(<< - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; - -/** - * Exception patch for HHVM to remove the stubs from special methods - * - * @author Christophe Coevoet - */ -class HhvmExceptionPatch implements ClassPatchInterface -{ - /** - * Supports exceptions on HHVM. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - if (!defined('HHVM_VERSION')) { - return false; - } - - return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception'); - } - - /** - * Removes special exception static methods from the doubled methods. - * - * @param ClassNode $node - * - * @return void - */ - public function apply(ClassNode $node) - { - if ($node->hasMethod('setTraceOptions')) { - $node->getMethod('setTraceOptions')->useParentCode(); - } - if ($node->hasMethod('getTraceOptions')) { - $node->getMethod('getTraceOptions')->useParentCode(); - } - } - - /** - * {@inheritdoc} - */ - public function getPriority() - { - return -50; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; - -/** - * Class patch interface. - * Class patches extend doubles functionality or help - * Prophecy to avoid some internal PHP bugs. - * - * @author Konstantin Kudryashov - */ -interface ClassPatchInterface -{ - /** - * Checks if patch supports specific class node. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node); - - /** - * Applies patch to the specific class node. - * - * @param ClassNode $node - * @return void - */ - public function apply(ClassNode $node); - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority(); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\Doubler\Generator\Node\ArgumentNode; - -/** - * Add Prophecy functionality to the double. - * This is a core class patch for Prophecy. - * - * @author Konstantin Kudryashov - */ -class ProphecySubjectPatch implements ClassPatchInterface -{ - /** - * Always returns true. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - return true; - } - - /** - * Apply Prophecy functionality to class node. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface'); - $node->addProperty('objectProphecy', 'private'); - - foreach ($node->getMethods() as $name => $method) { - if ('__construct' === strtolower($name)) { - continue; - } - - if ($method->getReturnType() === 'void') { - $method->setCode( - '$this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' - ); - } else { - $method->setCode( - 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' - ); - } - } - - $prophecySetter = new MethodNode('setProphecy'); - $prophecyArgument = new ArgumentNode('prophecy'); - $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface'); - $prophecySetter->addArgument($prophecyArgument); - $prophecySetter->setCode('$this->objectProphecy = $prophecy;'); - - $prophecyGetter = new MethodNode('getProphecy'); - $prophecyGetter->setCode('return $this->objectProphecy;'); - - if ($node->hasMethod('__call')) { - $__call = $node->getMethod('__call'); - } else { - $__call = new MethodNode('__call'); - $__call->addArgument(new ArgumentNode('name')); - $__call->addArgument(new ArgumentNode('arguments')); - - $node->addMethod($__call, true); - } - - $__call->setCode(<<getProphecy(), func_get_arg(0) -); -PHP - ); - - $node->addMethod($prophecySetter, true); - $node->addMethod($prophecyGetter, true); - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 0; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; - -/** - * Remove method functionality from the double which will clash with php keywords. - * - * @author Milan Magudia - */ -class KeywordPatch implements ClassPatchInterface -{ - /** - * Support any class - * - * @param ClassNode $node - * - * @return boolean - */ - public function supports(ClassNode $node) - { - return true; - } - - /** - * Remove methods that clash with php keywords - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - $methodNames = array_keys($node->getMethods()); - $methodsToRemove = array_intersect($methodNames, $this->getKeywords()); - foreach ($methodsToRemove as $methodName) { - $node->removeMethod($methodName); - } - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 49; - } - - /** - * Returns array of php keywords. - * - * @return array - */ - private function getKeywords() - { - if (\PHP_VERSION_ID >= 70000) { - return array('__halt_compiler'); - } - - return array( - '__halt_compiler', - 'abstract', - 'and', - 'array', - 'as', - 'break', - 'callable', - 'case', - 'catch', - 'class', - 'clone', - 'const', - 'continue', - 'declare', - 'default', - 'die', - 'do', - 'echo', - 'else', - 'elseif', - 'empty', - 'enddeclare', - 'endfor', - 'endforeach', - 'endif', - 'endswitch', - 'endwhile', - 'eval', - 'exit', - 'extends', - 'final', - 'finally', - 'for', - 'foreach', - 'function', - 'global', - 'goto', - 'if', - 'implements', - 'include', - 'include_once', - 'instanceof', - 'insteadof', - 'interface', - 'isset', - 'list', - 'namespace', - 'new', - 'or', - 'print', - 'private', - 'protected', - 'public', - 'require', - 'require_once', - 'return', - 'static', - 'switch', - 'throw', - 'trait', - 'try', - 'unset', - 'use', - 'var', - 'while', - 'xor', - 'yield', - ); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; - -/** - * Traversable interface patch. - * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. - * - * @author Konstantin Kudryashov - */ -class TraversablePatch implements ClassPatchInterface -{ - /** - * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - if (in_array('Iterator', $node->getInterfaces())) { - return false; - } - if (in_array('IteratorAggregate', $node->getInterfaces())) { - return false; - } - - foreach ($node->getInterfaces() as $interface) { - if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) { - continue; - } - if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) { - continue; - } - if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) { - continue; - } - - return true; - } - - return false; - } - - /** - * Forces class to implement Iterator interface. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - $node->addInterface('Iterator'); - - $node->addMethod(new MethodNode('current')); - $node->addMethod(new MethodNode('key')); - $node->addMethod(new MethodNode('next')); - $node->addMethod(new MethodNode('rewind')); - $node->addMethod(new MethodNode('valid')); - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 100; - } -} -implementsAThrowableInterface($node) && $this->doesNotExtendAThrowableClass($node); - } - - /** - * @param ClassNode $node - * @return bool - */ - private function implementsAThrowableInterface(ClassNode $node) - { - foreach ($node->getInterfaces() as $type) { - if (is_a($type, 'Throwable', true)) { - return true; - } - } - - return false; - } - - /** - * @param ClassNode $node - * @return bool - */ - private function doesNotExtendAThrowableClass(ClassNode $node) - { - return !is_a($node->getParentClass(), 'Throwable', true); - } - - /** - * Applies patch to the specific class node. - * - * @param ClassNode $node - * - * @return void - */ - public function apply(ClassNode $node) - { - $this->checkItCanBeDoubled($node); - $this->setParentClassToException($node); - } - - private function checkItCanBeDoubled(ClassNode $node) - { - $className = $node->getParentClass(); - if ($className !== 'stdClass') { - throw new ClassCreatorException( - sprintf( - 'Cannot double concrete class %s as well as implement Traversable', - $className - ), - $node - ); - } - } - - private function setParentClassToException(ClassNode $node) - { - $node->setParentClass('Exception'); - - $node->removeMethod('getMessage'); - $node->removeMethod('getCode'); - $node->removeMethod('getFile'); - $node->removeMethod('getLine'); - $node->removeMethod('getTrace'); - $node->removeMethod('getPrevious'); - $node->removeMethod('getNext'); - $node->removeMethod('getTraceAsString'); - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 100; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; - -/** - * ReflectionClass::newInstance patch. - * Makes first argument of newInstance optional, since it works but signature is misleading - * - * @author Florian Klein - */ -class ReflectionClassNewInstancePatch implements ClassPatchInterface -{ - /** - * Supports ReflectionClass - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - return 'ReflectionClass' === $node->getParentClass(); - } - - /** - * Updates newInstance's first argument to make it optional - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - foreach ($node->getMethod('newInstance')->getArguments() as $argument) { - $argument->setDefault(null); - } - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher = earlier) - */ - public function getPriority() - { - return 50; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; - -/** - * SplFileInfo patch. - * Makes SplFileInfo and derivative classes usable with Prophecy. - * - * @author Konstantin Kudryashov - */ -class SplFileInfoPatch implements ClassPatchInterface -{ - /** - * Supports everything that extends SplFileInfo. - * - * @param ClassNode $node - * - * @return bool - */ - public function supports(ClassNode $node) - { - if (null === $node->getParentClass()) { - return false; - } - return 'SplFileInfo' === $node->getParentClass() - || is_subclass_of($node->getParentClass(), 'SplFileInfo') - ; - } - - /** - * Updated constructor code to call parent one with dummy file argument. - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - if ($node->hasMethod('__construct')) { - $constructor = $node->getMethod('__construct'); - } else { - $constructor = new MethodNode('__construct'); - $node->addMethod($constructor); - } - - if ($this->nodeIsDirectoryIterator($node)) { - $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); - - return; - } - - if ($this->nodeIsSplFileObject($node)) { - $filePath = str_replace('\\','\\\\',__FILE__); - $constructor->setCode('return parent::__construct("' . $filePath .'");'); - - return; - } - - if ($this->nodeIsSymfonySplFileInfo($node)) { - $filePath = str_replace('\\','\\\\',__FILE__); - $constructor->setCode('return parent::__construct("' . $filePath .'", "", "");'); - - return; - } - - $constructor->useParentCode(); - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return int Priority number (higher - earlier) - */ - public function getPriority() - { - return 50; - } - - /** - * @param ClassNode $node - * @return boolean - */ - private function nodeIsDirectoryIterator(ClassNode $node) - { - $parent = $node->getParentClass(); - - return 'DirectoryIterator' === $parent - || is_subclass_of($parent, 'DirectoryIterator'); - } - - /** - * @param ClassNode $node - * @return boolean - */ - private function nodeIsSplFileObject(ClassNode $node) - { - $parent = $node->getParentClass(); - - return 'SplFileObject' === $parent - || is_subclass_of($parent, 'SplFileObject'); - } - - /** - * @param ClassNode $node - * @return boolean - */ - private function nodeIsSymfonySplFileInfo(ClassNode $node) - { - $parent = $node->getParentClass(); - - return 'Symfony\\Component\\Finder\\SplFileInfo' === $parent; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler\ClassPatch; - -use Prophecy\Doubler\Generator\Node\ClassNode; -use Prophecy\Doubler\Generator\Node\MethodNode; -use Prophecy\PhpDocumentor\ClassAndInterfaceTagRetriever; -use Prophecy\PhpDocumentor\MethodTagRetrieverInterface; - -/** - * Discover Magical API using "@method" PHPDoc format. - * - * @author Thomas Tourlourat - * @author Kévin Dunglas - * @author Théo FIDRY - */ -class MagicCallPatch implements ClassPatchInterface -{ - private $tagRetriever; - - public function __construct(MethodTagRetrieverInterface $tagRetriever = null) - { - $this->tagRetriever = null === $tagRetriever ? new ClassAndInterfaceTagRetriever() : $tagRetriever; - } - - /** - * Support any class - * - * @param ClassNode $node - * - * @return boolean - */ - public function supports(ClassNode $node) - { - return true; - } - - /** - * Discover Magical API - * - * @param ClassNode $node - */ - public function apply(ClassNode $node) - { - $types = array_filter($node->getInterfaces(), function ($interface) { - return 0 !== strpos($interface, 'Prophecy\\'); - }); - $types[] = $node->getParentClass(); - - foreach ($types as $type) { - $reflectionClass = new \ReflectionClass($type); - - while ($reflectionClass) { - $tagList = $this->tagRetriever->getTagList($reflectionClass); - - foreach ($tagList as $tag) { - $methodName = $tag->getMethodName(); - - if (empty($methodName)) { - continue; - } - - if (!$reflectionClass->hasMethod($methodName)) { - $methodNode = new MethodNode($methodName); - $methodNode->setStatic($tag->isStatic()); - $node->addMethod($methodNode); - } - } - - $reflectionClass = $reflectionClass->getParentClass(); - } - } - } - - /** - * Returns patch priority, which determines when patch will be applied. - * - * @return integer Priority number (higher - earlier) - */ - public function getPriority() - { - return 50; - } -} - - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Doubler; - -use Prophecy\Exception\Doubler\DoubleException; -use Prophecy\Exception\Doubler\ClassNotFoundException; -use Prophecy\Exception\Doubler\InterfaceNotFoundException; -use ReflectionClass; - -/** - * Lazy double. - * Gives simple interface to describe double before creating it. - * - * @author Konstantin Kudryashov - */ -class LazyDouble -{ - private $doubler; - private $class; - private $interfaces = array(); - private $arguments = null; - private $double; - - /** - * Initializes lazy double. - * - * @param Doubler $doubler - */ - public function __construct(Doubler $doubler) - { - $this->doubler = $doubler; - } - - /** - * Tells doubler to use specific class as parent one for double. - * - * @param string|ReflectionClass $class - * - * @throws \Prophecy\Exception\Doubler\ClassNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException - */ - public function setParentClass($class) - { - if (null !== $this->double) { - throw new DoubleException('Can not extend class with already instantiated double.'); - } - - if (!$class instanceof ReflectionClass) { - if (!class_exists($class)) { - throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class); - } - - $class = new ReflectionClass($class); - } - - $this->class = $class; - } - - /** - * Tells doubler to implement specific interface with double. - * - * @param string|ReflectionClass $interface - * - * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException - * @throws \Prophecy\Exception\Doubler\DoubleException - */ - public function addInterface($interface) - { - if (null !== $this->double) { - throw new DoubleException( - 'Can not implement interface with already instantiated double.' - ); - } - - if (!$interface instanceof ReflectionClass) { - if (!interface_exists($interface)) { - throw new InterfaceNotFoundException( - sprintf('Interface %s not found.', $interface), - $interface - ); - } - - $interface = new ReflectionClass($interface); - } - - $this->interfaces[] = $interface; - } - - /** - * Sets constructor arguments. - * - * @param array $arguments - */ - public function setArguments(array $arguments = null) - { - $this->arguments = $arguments; - } - - /** - * Creates double instance or returns already created one. - * - * @return DoubleInterface - */ - public function getInstance() - { - if (null === $this->double) { - if (null !== $this->arguments) { - return $this->double = $this->doubler->double( - $this->class, $this->interfaces, $this->arguments - ); - } - - $this->double = $this->doubler->double($this->class, $this->interfaces); - } - - return $this->double; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy; - -use Prophecy\Argument\Token; - -/** - * Argument tokens shortcuts. - * - * @author Konstantin Kudryashov - */ -class Argument -{ - /** - * Checks that argument is exact value or object. - * - * @param mixed $value - * - * @return Token\ExactValueToken - */ - public static function exact($value) - { - return new Token\ExactValueToken($value); - } - - /** - * Checks that argument is of specific type or instance of specific class. - * - * @param string $type Type name (`integer`, `string`) or full class name - * - * @return Token\TypeToken - */ - public static function type($type) - { - return new Token\TypeToken($type); - } - - /** - * Checks that argument object has specific state. - * - * @param string $methodName - * @param mixed $value - * - * @return Token\ObjectStateToken - */ - public static function which($methodName, $value) - { - return new Token\ObjectStateToken($methodName, $value); - } - - /** - * Checks that argument matches provided callback. - * - * @param callable $callback - * - * @return Token\CallbackToken - */ - public static function that($callback) - { - return new Token\CallbackToken($callback); - } - - /** - * Matches any single value. - * - * @return Token\AnyValueToken - */ - public static function any() - { - return new Token\AnyValueToken; - } - - /** - * Matches all values to the rest of the signature. - * - * @return Token\AnyValuesToken - */ - public static function cetera() - { - return new Token\AnyValuesToken; - } - - /** - * Checks that argument matches all tokens - * - * @param mixed ... a list of tokens - * - * @return Token\LogicalAndToken - */ - public static function allOf() - { - return new Token\LogicalAndToken(func_get_args()); - } - - /** - * Checks that argument array or countable object has exact number of elements. - * - * @param integer $value array elements count - * - * @return Token\ArrayCountToken - */ - public static function size($value) - { - return new Token\ArrayCountToken($value); - } - - /** - * Checks that argument array contains (key, value) pair - * - * @param mixed $key exact value or token - * @param mixed $value exact value or token - * - * @return Token\ArrayEntryToken - */ - public static function withEntry($key, $value) - { - return new Token\ArrayEntryToken($key, $value); - } - - /** - * Checks that arguments array entries all match value - * - * @param mixed $value - * - * @return Token\ArrayEveryEntryToken - */ - public static function withEveryEntry($value) - { - return new Token\ArrayEveryEntryToken($value); - } - - /** - * Checks that argument array contains value - * - * @param mixed $value - * - * @return Token\ArrayEntryToken - */ - public static function containing($value) - { - return new Token\ArrayEntryToken(self::any(), $value); - } - - /** - * Checks that argument array has key - * - * @param mixed $key exact value or token - * - * @return Token\ArrayEntryToken - */ - public static function withKey($key) - { - return new Token\ArrayEntryToken($key, self::any()); - } - - /** - * Checks that argument does not match the value|token. - * - * @param mixed $value either exact value or argument token - * - * @return Token\LogicalNotToken - */ - public static function not($value) - { - return new Token\LogicalNotToken($value); - } - - /** - * @param string $value - * - * @return Token\StringContainsToken - */ - public static function containingString($value) - { - return new Token\StringContainsToken($value); - } - - /** - * Checks that argument is identical value. - * - * @param mixed $value - * - * @return Token\IdenticalValueToken - */ - public static function is($value) - { - return new Token\IdenticalValueToken($value); - } - - /** - * Check that argument is same value when rounding to the - * given precision. - * - * @param float $value - * @param float $precision - * - * @return Token\ApproximateValueToken - */ - public static function approximate($value, $precision = 0) - { - return new Token\ApproximateValueToken($value, $precision); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Comparator; - -use Prophecy\Prophecy\ProphecyInterface; -use SebastianBergmann\Comparator\ObjectComparator; - -class ProphecyComparator extends ObjectComparator -{ - public function accepts($expected, $actual) - { - return is_object($expected) && is_object($actual) && $actual instanceof ProphecyInterface; - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) - { - parent::assertEquals($expected, $actual->reveal(), $delta, $canonicalize, $ignoreCase, $processed); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Comparator; - -use SebastianBergmann\Comparator\Factory as BaseFactory; - -/** - * Prophecy comparator factory. - * - * @author Konstantin Kudryashov - */ -final class Factory extends BaseFactory -{ - /** - * @var Factory - */ - private static $instance; - - public function __construct() - { - parent::__construct(); - - $this->register(new ClosureComparator()); - $this->register(new ProphecyComparator()); - } - - /** - * @return Factory - */ - public static function getInstance() - { - if (self::$instance === null) { - self::$instance = new Factory; - } - - return self::$instance; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Comparator; - -use SebastianBergmann\Comparator\Comparator; -use SebastianBergmann\Comparator\ComparisonFailure; - -/** - * Closure comparator. - * - * @author Konstantin Kudryashov - */ -final class ClosureComparator extends Comparator -{ - public function accepts($expected, $actual) - { - return is_object($expected) && $expected instanceof \Closure - && is_object($actual) && $actual instanceof \Closure; - } - - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - throw new ComparisonFailure( - $expected, - $actual, - // we don't need a diff - '', - '', - false, - 'all closures are born different' - ); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prediction; - -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Argument\Token\AnyValuesToken; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\UnexpectedCallsCountException; - -/** - * Prediction interface. - * Predictions are logical test blocks, tied to `should...` keyword. - * - * @author Konstantin Kudryashov - */ -class CallTimesPrediction implements PredictionInterface -{ - private $times; - private $util; - - /** - * Initializes prediction. - * - * @param int $times - * @param StringUtil $util - */ - public function __construct($times, StringUtil $util = null) - { - $this->times = intval($times); - $this->util = $util ?: new StringUtil; - } - - /** - * Tests that there was exact amount of calls made. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException - */ - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - if ($this->times == count($calls)) { - return; - } - - $methodCalls = $object->findProphecyMethodCalls( - $method->getMethodName(), - new ArgumentsWildcard(array(new AnyValuesToken)) - ); - - if (count($calls)) { - $message = sprintf( - "Expected exactly %d calls that match:\n". - " %s->%s(%s)\n". - "but %d were made:\n%s", - - $this->times, - get_class($object->reveal()), - $method->getMethodName(), - $method->getArgumentsWildcard(), - count($calls), - $this->util->stringifyCalls($calls) - ); - } elseif (count($methodCalls)) { - $message = sprintf( - "Expected exactly %d calls that match:\n". - " %s->%s(%s)\n". - "but none were made.\n". - "Recorded `%s(...)` calls:\n%s", - - $this->times, - get_class($object->reveal()), - $method->getMethodName(), - $method->getArgumentsWildcard(), - $method->getMethodName(), - $this->util->stringifyCalls($methodCalls) - ); - } else { - $message = sprintf( - "Expected exactly %d calls that match:\n". - " %s->%s(%s)\n". - "but none were made.", - - $this->times, - get_class($object->reveal()), - $method->getMethodName(), - $method->getArgumentsWildcard() - ); - } - - throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prediction; - -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Argument\Token\AnyValuesToken; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\NoCallsException; - -/** - * Call prediction. - * - * @author Konstantin Kudryashov - */ -class CallPrediction implements PredictionInterface -{ - private $util; - - /** - * Initializes prediction. - * - * @param StringUtil $util - */ - public function __construct(StringUtil $util = null) - { - $this->util = $util ?: new StringUtil; - } - - /** - * Tests that there was at least one call. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws \Prophecy\Exception\Prediction\NoCallsException - */ - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - if (count($calls)) { - return; - } - - $methodCalls = $object->findProphecyMethodCalls( - $method->getMethodName(), - new ArgumentsWildcard(array(new AnyValuesToken)) - ); - - if (count($methodCalls)) { - throw new NoCallsException(sprintf( - "No calls have been made that match:\n". - " %s->%s(%s)\n". - "but expected at least one.\n". - "Recorded `%s(...)` calls:\n%s", - - get_class($object->reveal()), - $method->getMethodName(), - $method->getArgumentsWildcard(), - $method->getMethodName(), - $this->util->stringifyCalls($methodCalls) - ), $method); - } - - throw new NoCallsException(sprintf( - "No calls have been made that match:\n". - " %s->%s(%s)\n". - "but expected at least one.", - - get_class($object->reveal()), - $method->getMethodName(), - $method->getArgumentsWildcard() - ), $method); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prediction; - -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use Closure; - -/** - * Callback prediction. - * - * @author Konstantin Kudryashov - */ -class CallbackPrediction implements PredictionInterface -{ - private $callback; - - /** - * Initializes callback prediction. - * - * @param callable $callback Custom callback - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException(sprintf( - 'Callable expected as an argument to CallbackPrediction, but got %s.', - gettype($callback) - )); - } - - $this->callback = $callback; - } - - /** - * Executes preset callback. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - */ - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - $callback = $this->callback; - - if ($callback instanceof Closure && method_exists('Closure', 'bind')) { - $callback = Closure::bind($callback, $object); - } - - call_user_func($callback, $calls, $object, $method); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prediction; - -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\UnexpectedCallsException; - -/** - * No calls prediction. - * - * @author Konstantin Kudryashov - */ -class NoCallsPrediction implements PredictionInterface -{ - private $util; - - /** - * Initializes prediction. - * - * @param null|StringUtil $util - */ - public function __construct(StringUtil $util = null) - { - $this->util = $util ?: new StringUtil; - } - - /** - * Tests that there were no calls made. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException - */ - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) - { - if (!count($calls)) { - return; - } - - $verb = count($calls) === 1 ? 'was' : 'were'; - - throw new UnexpectedCallsException(sprintf( - "No calls expected that match:\n". - " %s->%s(%s)\n". - "but %d %s made:\n%s", - get_class($object->reveal()), - $method->getMethodName(), - $method->getArgumentsWildcard(), - count($calls), - $verb, - $this->util->stringifyCalls($calls) - ), $method, $calls); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Prediction; - -use Prophecy\Call\Call; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; - -/** - * Prediction interface. - * Predictions are logical test blocks, tied to `should...` keyword. - * - * @author Konstantin Kudryashov - */ -interface PredictionInterface -{ - /** - * Tests that double fulfilled prediction. - * - * @param Call[] $calls - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws object - * @return void - */ - public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy; - -use Prophecy\Doubler\Doubler; -use Prophecy\Doubler\LazyDouble; -use Prophecy\Doubler\ClassPatch; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\RevealerInterface; -use Prophecy\Prophecy\Revealer; -use Prophecy\Call\CallCenter; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Exception\Prediction\AggregateException; - -/** - * Prophet creates prophecies. - * - * @author Konstantin Kudryashov - */ -class Prophet -{ - private $doubler; - private $revealer; - private $util; - - /** - * @var ObjectProphecy[] - */ - private $prophecies = array(); - - /** - * Initializes Prophet. - * - * @param null|Doubler $doubler - * @param null|RevealerInterface $revealer - * @param null|StringUtil $util - */ - public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, - StringUtil $util = null) - { - if (null === $doubler) { - $doubler = new Doubler; - $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch); - $doubler->registerClassPatch(new ClassPatch\TraversablePatch); - $doubler->registerClassPatch(new ClassPatch\ThrowablePatch); - $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch); - $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch); - $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch); - $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); - $doubler->registerClassPatch(new ClassPatch\MagicCallPatch); - $doubler->registerClassPatch(new ClassPatch\KeywordPatch); - } - - $this->doubler = $doubler; - $this->revealer = $revealer ?: new Revealer; - $this->util = $util ?: new StringUtil; - } - - /** - * Creates new object prophecy. - * - * @param null|string $classOrInterface Class or interface name - * - * @return ObjectProphecy - */ - public function prophesize($classOrInterface = null) - { - $this->prophecies[] = $prophecy = new ObjectProphecy( - new LazyDouble($this->doubler), - new CallCenter($this->util), - $this->revealer - ); - - if ($classOrInterface && class_exists($classOrInterface)) { - return $prophecy->willExtend($classOrInterface); - } - - if ($classOrInterface && interface_exists($classOrInterface)) { - return $prophecy->willImplement($classOrInterface); - } - - return $prophecy; - } - - /** - * Returns all created object prophecies. - * - * @return ObjectProphecy[] - */ - public function getProphecies() - { - return $this->prophecies; - } - - /** - * Returns Doubler instance assigned to this Prophet. - * - * @return Doubler - */ - public function getDoubler() - { - return $this->doubler; - } - - /** - * Checks all predictions defined by prophecies of this Prophet. - * - * @throws Exception\Prediction\AggregateException If any prediction fails - */ - public function checkPredictions() - { - $exception = new AggregateException("Some predictions failed:\n"); - foreach ($this->prophecies as $prophecy) { - try { - $prophecy->checkProphecyMethodsPredictions(); - } catch (PredictionException $e) { - $exception->append($e); - } - } - - if (count($exception->getExceptions())) { - throw $exception; - } - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\PhpDocumentor; - -use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; -use phpDocumentor\Reflection\DocBlock\Tags\Method; - -/** - * @author Théo FIDRY - * - * @internal - */ -final class ClassAndInterfaceTagRetriever implements MethodTagRetrieverInterface -{ - private $classRetriever; - - public function __construct(MethodTagRetrieverInterface $classRetriever = null) - { - if (null !== $classRetriever) { - $this->classRetriever = $classRetriever; - - return; - } - - $this->classRetriever = class_exists('phpDocumentor\Reflection\DocBlockFactory') && class_exists('phpDocumentor\Reflection\Types\ContextFactory') - ? new ClassTagRetriever() - : new LegacyClassTagRetriever() - ; - } - - /** - * @param \ReflectionClass $reflectionClass - * - * @return LegacyMethodTag[]|Method[] - */ - public function getTagList(\ReflectionClass $reflectionClass) - { - return array_merge( - $this->classRetriever->getTagList($reflectionClass), - $this->getInterfacesTagList($reflectionClass) - ); - } - - /** - * @param \ReflectionClass $reflectionClass - * - * @return LegacyMethodTag[]|Method[] - */ - private function getInterfacesTagList(\ReflectionClass $reflectionClass) - { - $interfaces = $reflectionClass->getInterfaces(); - $tagList = array(); - - foreach($interfaces as $interface) { - $tagList = array_merge($tagList, $this->classRetriever->getTagList($interface)); - } - - return $tagList; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\PhpDocumentor; - -use phpDocumentor\Reflection\DocBlock\Tags\Method; -use phpDocumentor\Reflection\DocBlockFactory; -use phpDocumentor\Reflection\Types\ContextFactory; - -/** - * @author Théo FIDRY - * - * @internal - */ -final class ClassTagRetriever implements MethodTagRetrieverInterface -{ - private $docBlockFactory; - private $contextFactory; - - public function __construct() - { - $this->docBlockFactory = DocBlockFactory::createInstance(); - $this->contextFactory = new ContextFactory(); - } - - /** - * @param \ReflectionClass $reflectionClass - * - * @return Method[] - */ - public function getTagList(\ReflectionClass $reflectionClass) - { - try { - $phpdoc = $this->docBlockFactory->create( - $reflectionClass, - $this->contextFactory->createFromReflector($reflectionClass) - ); - - return $phpdoc->getTagsByName('method'); - } catch (\InvalidArgumentException $e) { - return array(); - } - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\PhpDocumentor; - -use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; -use phpDocumentor\Reflection\DocBlock\Tags\Method; - -/** - * @author Théo FIDRY - * - * @internal - */ -interface MethodTagRetrieverInterface -{ - /** - * @param \ReflectionClass $reflectionClass - * - * @return LegacyMethodTag[]|Method[] - */ - public function getTagList(\ReflectionClass $reflectionClass); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\PhpDocumentor; - -use phpDocumentor\Reflection\DocBlock; -use phpDocumentor\Reflection\DocBlock\Tag\MethodTag as LegacyMethodTag; - -/** - * @author Théo FIDRY - * - * @internal - */ -final class LegacyClassTagRetriever implements MethodTagRetrieverInterface -{ - /** - * @param \ReflectionClass $reflectionClass - * - * @return LegacyMethodTag[] - */ - public function getTagList(\ReflectionClass $reflectionClass) - { - $phpdoc = new DocBlock($reflectionClass->getDocComment()); - - return $phpdoc->getTagsByName('method'); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * This class is a modification from sebastianbergmann/exporter - * @see https://github.com/sebastianbergmann/exporter - */ -class ExportUtil -{ - /** - * Exports a value as a string - * - * The output of this method is similar to the output of print_r(), but - * improved in various aspects: - * - * - NULL is rendered as "null" (instead of "") - * - TRUE is rendered as "true" (instead of "1") - * - FALSE is rendered as "false" (instead of "") - * - Strings are always quoted with single quotes - * - Carriage returns and newlines are normalized to \n - * - Recursion and repeated rendering is treated properly - * - * @param mixed $value - * @param int $indentation The indentation level of the 2nd+ line - * @return string - */ - public static function export($value, $indentation = 0) - { - return self::recursiveExport($value, $indentation); - } - - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param mixed $value - * @return array - */ - public static function toArray($value) - { - if (!is_object($value)) { - return (array) $value; - } - - $array = array(); - - foreach ((array) $value as $key => $val) { - // properties are transformed to keys in the following way: - // private $property => "\0Classname\0property" - // protected $property => "\0*\0property" - // public $property => "property" - if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { - $key = $matches[1]; - } - - // See https://github.com/php/php-src/commit/5721132 - if ($key === "\0gcdata") { - continue; - } - - $array[$key] = $val; - } - - // Some internal classes like SplObjectStorage don't work with the - // above (fast) mechanism nor with reflection in Zend. - // Format the output similarly to print_r() in this case - if ($value instanceof \SplObjectStorage) { - // However, the fast method does work in HHVM, and exposes the - // internal implementation. Hide it again. - if (property_exists('\SplObjectStorage', '__storage')) { - unset($array['__storage']); - } elseif (property_exists('\SplObjectStorage', 'storage')) { - unset($array['storage']); - } - - if (property_exists('\SplObjectStorage', '__key')) { - unset($array['__key']); - } - - foreach ($value as $key => $val) { - $array[spl_object_hash($val)] = array( - 'obj' => $val, - 'inf' => $value->getInfo(), - ); - } - } - - return $array; - } - - /** - * Recursive implementation of export - * - * @param mixed $value The value to export - * @param int $indentation The indentation level of the 2nd+ line - * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects - * @return string - * @see SebastianBergmann\Exporter\Exporter::export - */ - protected static function recursiveExport(&$value, $indentation, $processed = null) - { - if ($value === null) { - return 'null'; - } - - if ($value === true) { - return 'true'; - } - - if ($value === false) { - return 'false'; - } - - if (is_float($value) && floatval(intval($value)) === $value) { - return "$value.0"; - } - - if (is_resource($value)) { - return sprintf( - 'resource(%d) of type (%s)', - $value, - get_resource_type($value) - ); - } - - if (is_string($value)) { - // Match for most non printable chars somewhat taking multibyte chars into account - if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { - return 'Binary String: 0x' . bin2hex($value); - } - - return "'" . - str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . - "'"; - } - - $whitespace = str_repeat(' ', 4 * $indentation); - - if (!$processed) { - $processed = new Context; - } - - if (is_array($value)) { - if (($key = $processed->contains($value)) !== false) { - return 'Array &' . $key; - } - - $array = $value; - $key = $processed->add($value); - $values = ''; - - if (count($array) > 0) { - foreach ($array as $k => $v) { - $values .= sprintf( - '%s %s => %s' . "\n", - $whitespace, - self::recursiveExport($k, $indentation), - self::recursiveExport($value[$k], $indentation + 1, $processed) - ); - } - - $values = "\n" . $values . $whitespace; - } - - return sprintf('Array &%s (%s)', $key, $values); - } - - if (is_object($value)) { - $class = get_class($value); - - if ($value instanceof ProphecyInterface) { - return sprintf('%s Object (*Prophecy*)', $class); - } elseif ($hash = $processed->contains($value)) { - return sprintf('%s:%s Object', $class, $hash); - } - - $hash = $processed->add($value); - $values = ''; - $array = self::toArray($value); - - if (count($array) > 0) { - foreach ($array as $k => $v) { - $values .= sprintf( - '%s %s => %s' . "\n", - $whitespace, - self::recursiveExport($k, $indentation), - self::recursiveExport($v, $indentation + 1, $processed) - ); - } - - $values = "\n" . $values . $whitespace; - } - - return sprintf('%s:%s Object (%s)', $class, $hash, $values); - } - - return var_export($value, true); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Util; - -use Prophecy\Call\Call; - -/** - * String utility. - * - * @author Konstantin Kudryashov - */ -class StringUtil -{ - private $verbose; - - /** - * @param bool $verbose - */ - public function __construct($verbose = true) - { - $this->verbose = $verbose; - } - - /** - * Stringifies any provided value. - * - * @param mixed $value - * @param boolean $exportObject - * - * @return string - */ - public function stringify($value, $exportObject = true) - { - if (is_array($value)) { - if (range(0, count($value) - 1) === array_keys($value)) { - return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']'; - } - - $stringify = array($this, __FUNCTION__); - - return '['.implode(', ', array_map(function ($item, $key) use ($stringify) { - return (is_integer($key) ? $key : '"'.$key.'"'). - ' => '.call_user_func($stringify, $item); - }, $value, array_keys($value))).']'; - } - if (is_resource($value)) { - return get_resource_type($value).':'.$value; - } - if (is_object($value)) { - return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value)); - } - if (true === $value || false === $value) { - return $value ? 'true' : 'false'; - } - if (is_string($value)) { - $str = sprintf('"%s"', str_replace("\n", '\\n', $value)); - - if (!$this->verbose && 50 <= strlen($str)) { - return substr($str, 0, 50).'"...'; - } - - return $str; - } - if (null === $value) { - return 'null'; - } - - return (string) $value; - } - - /** - * Stringifies provided array of calls. - * - * @param Call[] $calls Array of Call instances - * - * @return string - */ - public function stringifyCalls(array $calls) - { - $self = $this; - - return implode(PHP_EOL, array_map(function (Call $call) use ($self) { - return sprintf(' - %s(%s) @ %s', - $call->getMethodName(), - implode(', ', array_map(array($self, 'stringify'), $call->getArguments())), - str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace()) - ); - }, $calls)); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Call; - -use Prophecy\Exception\Prophecy\MethodProphecyException; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Argument\ArgumentsWildcard; -use Prophecy\Util\StringUtil; -use Prophecy\Exception\Call\UnexpectedCallException; - -/** - * Calls receiver & manager. - * - * @author Konstantin Kudryashov - */ -class CallCenter -{ - private $util; - - /** - * @var Call[] - */ - private $recordedCalls = array(); - - /** - * Initializes call center. - * - * @param StringUtil $util - */ - public function __construct(StringUtil $util = null) - { - $this->util = $util ?: new StringUtil; - } - - /** - * Makes and records specific method call for object prophecy. - * - * @param ObjectProphecy $prophecy - * @param string $methodName - * @param array $arguments - * - * @return mixed Returns null if no promise for prophecy found or promise return value. - * - * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found - */ - public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) - { - // For efficiency exclude 'args' from the generated backtrace - if (PHP_VERSION_ID >= 50400) { - // Limit backtrace to last 3 calls as we don't use the rest - // Limit argument was introduced in PHP 5.4.0 - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); - } elseif (defined('DEBUG_BACKTRACE_IGNORE_ARGS')) { - // DEBUG_BACKTRACE_IGNORE_ARGS was introduced in PHP 5.3.6 - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - } else { - $backtrace = debug_backtrace(); - } - - $file = $line = null; - if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { - $file = $backtrace[2]['file']; - $line = $backtrace[2]['line']; - } - - // If no method prophecies defined, then it's a dummy, so we'll just return null - if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) { - $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); - - return null; - } - - // There are method prophecies, so it's a fake/stub. Searching prophecy for this call - $matches = array(); - foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { - if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { - $matches[] = array($score, $methodProphecy); - } - } - - // If fake/stub doesn't have method prophecy for this call - throw exception - if (!count($matches)) { - throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments); - } - - // Sort matches by their score value - @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); - - $score = $matches[0][0]; - // If Highest rated method prophecy has a promise - execute it or return null instead - $methodProphecy = $matches[0][1]; - $returnValue = null; - $exception = null; - if ($promise = $methodProphecy->getPromise()) { - try { - $returnValue = $promise->execute($arguments, $prophecy, $methodProphecy); - } catch (\Exception $e) { - $exception = $e; - } - } - - if ($methodProphecy->hasReturnVoid() && $returnValue !== null) { - throw new MethodProphecyException( - "The method \"$methodName\" has a void return type, but the promise returned a value", - $methodProphecy - ); - } - - $this->recordedCalls[] = $call = new Call( - $methodName, $arguments, $returnValue, $exception, $file, $line - ); - $call->addScore($methodProphecy->getArgumentsWildcard(), $score); - - if (null !== $exception) { - throw $exception; - } - - return $returnValue; - } - - /** - * Searches for calls by method name & arguments wildcard. - * - * @param string $methodName - * @param ArgumentsWildcard $wildcard - * - * @return Call[] - */ - public function findCalls($methodName, ArgumentsWildcard $wildcard) - { - return array_values( - array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { - return $methodName === $call->getMethodName() - && 0 < $call->getScore($wildcard) - ; - }) - ); - } - - private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, - array $arguments) - { - $classname = get_class($prophecy->reveal()); - $indentationLength = 8; // looks good - $argstring = implode( - ",\n", - $this->indentArguments( - array_map(array($this->util, 'stringify'), $arguments), - $indentationLength - ) - ); - - $expected = array(); - - foreach (call_user_func_array('array_merge', $prophecy->getMethodProphecies()) as $methodProphecy) { - $expected[] = sprintf( - " - %s(\n" . - "%s\n" . - " )", - $methodProphecy->getMethodName(), - implode( - ",\n", - $this->indentArguments( - array_map('strval', $methodProphecy->getArgumentsWildcard()->getTokens()), - $indentationLength - ) - ) - ); - } - - return new UnexpectedCallException( - sprintf( - "Unexpected method call on %s:\n". - " - %s(\n". - "%s\n". - " )\n". - "expected calls were:\n". - "%s", - - $classname, $methodName, $argstring, implode("\n", $expected) - ), - $prophecy, $methodName, $arguments - - ); - } - - private function formatExceptionMessage(MethodProphecy $methodProphecy) - { - return sprintf( - " - %s(\n". - "%s\n". - " )", - $methodProphecy->getMethodName(), - implode( - ",\n", - $this->indentArguments( - array_map( - function ($token) { - return (string) $token; - }, - $methodProphecy->getArgumentsWildcard()->getTokens() - ), - $indentationLength - ) - ) - ); - } - - private function indentArguments(array $arguments, $indentationLength) - { - return preg_replace_callback( - '/^/m', - function () use ($indentationLength) { - return str_repeat(' ', $indentationLength); - }, - $arguments - ); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Call; - -use Exception; -use Prophecy\Argument\ArgumentsWildcard; - -/** - * Call object. - * - * @author Konstantin Kudryashov - */ -class Call -{ - private $methodName; - private $arguments; - private $returnValue; - private $exception; - private $file; - private $line; - private $scores; - - /** - * Initializes call. - * - * @param string $methodName - * @param array $arguments - * @param mixed $returnValue - * @param Exception $exception - * @param null|string $file - * @param null|int $line - */ - public function __construct($methodName, array $arguments, $returnValue, - Exception $exception = null, $file, $line) - { - $this->methodName = $methodName; - $this->arguments = $arguments; - $this->returnValue = $returnValue; - $this->exception = $exception; - $this->scores = new \SplObjectStorage(); - - if ($file) { - $this->file = $file; - $this->line = intval($line); - } - } - - /** - * Returns called method name. - * - * @return string - */ - public function getMethodName() - { - return $this->methodName; - } - - /** - * Returns called method arguments. - * - * @return array - */ - public function getArguments() - { - return $this->arguments; - } - - /** - * Returns called method return value. - * - * @return null|mixed - */ - public function getReturnValue() - { - return $this->returnValue; - } - - /** - * Returns exception that call thrown. - * - * @return null|Exception - */ - public function getException() - { - return $this->exception; - } - - /** - * Returns callee filename. - * - * @return string - */ - public function getFile() - { - return $this->file; - } - - /** - * Returns callee line number. - * - * @return int - */ - public function getLine() - { - return $this->line; - } - - /** - * Returns short notation for callee place. - * - * @return string - */ - public function getCallPlace() - { - if (null === $this->file) { - return 'unknown'; - } - - return sprintf('%s:%d', $this->file, $this->line); - } - - /** - * Adds the wildcard match score for the provided wildcard. - * - * @param ArgumentsWildcard $wildcard - * @param false|int $score - * - * @return $this - */ - public function addScore(ArgumentsWildcard $wildcard, $score) - { - $this->scores[$wildcard] = $score; - - return $this; - } - - /** - * Returns wildcard match score for the provided wildcard. The score is - * calculated if not already done. - * - * @param ArgumentsWildcard $wildcard - * - * @return false|int False OR integer score (higher - better) - */ - public function getScore(ArgumentsWildcard $wildcard) - { - if (isset($this->scores[$wildcard])) { - return $this->scores[$wildcard]; - } - - return $this->scores[$wildcard] = $wildcard->scoreArguments($this->getArguments()); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Promise; - -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; - -/** - * Return promise. - * - * @author Konstantin Kudryashov - */ -class ReturnPromise implements PromiseInterface -{ - private $returnValues = array(); - - /** - * Initializes promise. - * - * @param array $returnValues Array of values - */ - public function __construct(array $returnValues) - { - $this->returnValues = $returnValues; - } - - /** - * Returns saved values one by one until last one, then continuously returns last value. - * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @return mixed - */ - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - $value = array_shift($this->returnValues); - - if (!count($this->returnValues)) { - $this->returnValues[] = $value; - } - - return $value; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Promise; - -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; - -/** - * Promise interface. - * Promises are logical blocks, tied to `will...` keyword. - * - * @author Konstantin Kudryashov - */ -interface PromiseInterface -{ - /** - * Evaluates promise. - * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @return mixed - */ - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Promise; - -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use Closure; - -/** - * Callback promise. - * - * @author Konstantin Kudryashov - */ -class CallbackPromise implements PromiseInterface -{ - private $callback; - - /** - * Initializes callback promise. - * - * @param callable $callback Custom callback - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($callback) - { - if (!is_callable($callback)) { - throw new InvalidArgumentException(sprintf( - 'Callable expected as an argument to CallbackPromise, but got %s.', - gettype($callback) - )); - } - - $this->callback = $callback; - } - - /** - * Evaluates promise callback. - * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @return mixed - */ - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - $callback = $this->callback; - - if ($callback instanceof Closure && method_exists('Closure', 'bind')) { - $callback = Closure::bind($callback, $object); - } - - return call_user_func($callback, $args, $object, $method); - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Promise; - -use Prophecy\Exception\InvalidArgumentException; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; - -/** - * Return argument promise. - * - * @author Konstantin Kudryashov - */ -class ReturnArgumentPromise implements PromiseInterface -{ - /** - * @var int - */ - private $index; - - /** - * Initializes callback promise. - * - * @param int $index The zero-indexed number of the argument to return - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($index = 0) - { - if (!is_int($index) || $index < 0) { - throw new InvalidArgumentException(sprintf( - 'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', - $index - )); - } - $this->index = $index; - } - - /** - * Returns nth argument if has one, null otherwise. - * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @return null|mixed - */ - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - return count($args) > $this->index ? $args[$this->index] : null; - } -} - - * Marcello Duarte - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Prophecy\Promise; - -use Doctrine\Instantiator\Instantiator; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Exception\InvalidArgumentException; -use ReflectionClass; - -/** - * Throw promise. - * - * @author Konstantin Kudryashov - */ -class ThrowPromise implements PromiseInterface -{ - private $exception; - - /** - * @var \Doctrine\Instantiator\Instantiator - */ - private $instantiator; - - /** - * Initializes promise. - * - * @param string|\Exception|\Throwable $exception Exception class name or instance - * - * @throws \Prophecy\Exception\InvalidArgumentException - */ - public function __construct($exception) - { - if (is_string($exception)) { - if (!class_exists($exception) || !$this->isAValidThrowable($exception)) { - throw new InvalidArgumentException(sprintf( - 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', - $exception - )); - } - } elseif (!$exception instanceof \Exception && !$exception instanceof \Throwable) { - throw new InvalidArgumentException(sprintf( - 'Exception / Throwable class or instance expected as argument to ThrowPromise, but got %s.', - is_object($exception) ? get_class($exception) : gettype($exception) - )); - } - - $this->exception = $exception; - } - - /** - * Throws predefined exception. - * - * @param array $args - * @param ObjectProphecy $object - * @param MethodProphecy $method - * - * @throws object - */ - public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) - { - if (is_string($this->exception)) { - $classname = $this->exception; - $reflection = new ReflectionClass($classname); - $constructor = $reflection->getConstructor(); - - if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { - throw $reflection->newInstance(); - } - - if (!$this->instantiator) { - $this->instantiator = new Instantiator(); - } - - throw $this->instantiator->instantiate($classname); - } - - throw $this->exception; - } - - /** - * @param string $exception - * - * @return bool - */ - private function isAValidThrowable($exception) - { - return is_a($exception, 'Exception', true) || is_subclass_of($exception, 'Throwable', true); - } -} -Copyright (c) 2013 Konstantin Kudryashov - Marcello Duarte - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares Exception instances for equality. - */ -class ExceptionComparator extends ObjectComparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return $expected instanceof \Exception && $actual instanceof \Exception; - } - - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param object $object - * - * @return array - */ - protected function toArray($object) - { - $array = parent::toArray($object); - - unset( - $array['file'], - $array['line'], - $array['trace'], - $array['string'], - $array['xdebug_message'] - ); - - return $array; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares numerical values for equality. - */ -class NumericComparator extends ScalarComparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - // all numerical values, but not if one of them is a double - // or both of them are strings - return \is_numeric($expected) && \is_numeric($actual) && - !(\is_float($expected) || \is_float($actual)) && - !(\is_string($expected) && \is_string($actual)); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - if (\is_infinite($actual) && \is_infinite($expected)) { - return; // @codeCoverageIgnore - } - - if ((\is_infinite($actual) xor \is_infinite($expected)) || - (\is_nan($actual) || \is_nan($expected)) || - \abs($actual - $expected) > $delta) { - throw new ComparisonFailure( - $expected, - $actual, - '', - '', - false, - \sprintf( - 'Failed asserting that %s matches expected %s.', - $this->exporter->export($actual), - $this->exporter->export($expected) - ) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. - */ -class MockObjectComparator extends ObjectComparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return ($expected instanceof \PHPUnit_Framework_MockObject_MockObject || $expected instanceof \PHPUnit\Framework\MockObject\MockObject) && - ($actual instanceof \PHPUnit_Framework_MockObject_MockObject || $actual instanceof \PHPUnit\Framework\MockObject\MockObject); - } - - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param object $object - * - * @return array - */ - protected function toArray($object) - { - $array = parent::toArray($object); - - unset($array['__phpunit_invocationMocker']); - - return $array; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares values for type equality. - */ -class TypeComparator extends Comparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return true; - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - if (\gettype($expected) != \gettype($actual)) { - throw new ComparisonFailure( - $expected, - $actual, - // we don't need a diff - '', - '', - false, - \sprintf( - '%s does not match expected type "%s".', - $this->exporter->shortenedExport($actual), - \gettype($expected) - ) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares doubles for equality. - */ -class DoubleComparator extends NumericComparator -{ - /** - * Smallest value available in PHP. - * - * @var float - */ - const EPSILON = 0.0000000001; - - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return (\is_float($expected) || \is_float($actual)) && \is_numeric($expected) && \is_numeric($actual); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - if ($delta == 0) { - $delta = self::EPSILON; - } - - parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -use SebastianBergmann\Exporter\Exporter; - -/** - * Abstract base class for comparators which compare values for equality. - */ -abstract class Comparator -{ - /** - * @var Factory - */ - protected $factory; - - /** - * @var Exporter - */ - protected $exporter; - - public function __construct() - { - $this->exporter = new Exporter; - } - - public function setFactory(Factory $factory) - { - $this->factory = $factory; - } - - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - abstract public function accepts($expected, $actual); - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares arrays for equality. - */ -class ArrayComparator extends Comparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return \is_array($expected) && \is_array($actual); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) - { - if ($canonicalize) { - \sort($expected); - \sort($actual); - } - - $remaining = $actual; - $actualAsString = "Array (\n"; - $expectedAsString = "Array (\n"; - $equal = true; - - foreach ($expected as $key => $value) { - unset($remaining[$key]); - - if (!\array_key_exists($key, $actual)) { - $expectedAsString .= \sprintf( - " %s => %s\n", - $this->exporter->export($key), - $this->exporter->shortenedExport($value) - ); - - $equal = false; - - continue; - } - - try { - $comparator = $this->factory->getComparatorFor($value, $actual[$key]); - $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); - - $expectedAsString .= \sprintf( - " %s => %s\n", - $this->exporter->export($key), - $this->exporter->shortenedExport($value) - ); - - $actualAsString .= \sprintf( - " %s => %s\n", - $this->exporter->export($key), - $this->exporter->shortenedExport($actual[$key]) - ); - } catch (ComparisonFailure $e) { - $expectedAsString .= \sprintf( - " %s => %s\n", - $this->exporter->export($key), - $e->getExpectedAsString() ? $this->indent($e->getExpectedAsString()) : $this->exporter->shortenedExport($e->getExpected()) - ); - - $actualAsString .= \sprintf( - " %s => %s\n", - $this->exporter->export($key), - $e->getActualAsString() ? $this->indent($e->getActualAsString()) : $this->exporter->shortenedExport($e->getActual()) - ); - - $equal = false; - } - } - - foreach ($remaining as $key => $value) { - $actualAsString .= \sprintf( - " %s => %s\n", - $this->exporter->export($key), - $this->exporter->shortenedExport($value) - ); - - $equal = false; - } - - $expectedAsString .= ')'; - $actualAsString .= ')'; - - if (!$equal) { - throw new ComparisonFailure( - $expected, - $actual, - $expectedAsString, - $actualAsString, - false, - 'Failed asserting that two arrays are equal.' - ); - } - } - - protected function indent($lines) - { - return \trim(\str_replace("\n", "\n ", $lines)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -use SebastianBergmann\Diff\Differ; -use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; - -/** - * Thrown when an assertion for string equality failed. - */ -class ComparisonFailure extends \RuntimeException -{ - /** - * Expected value of the retrieval which does not match $actual. - * - * @var mixed - */ - protected $expected; - - /** - * Actually retrieved value which does not match $expected. - * - * @var mixed - */ - protected $actual; - - /** - * The string representation of the expected value - * - * @var string - */ - protected $expectedAsString; - - /** - * The string representation of the actual value - * - * @var string - */ - protected $actualAsString; - - /** - * @var bool - */ - protected $identical; - - /** - * Optional message which is placed in front of the first line - * returned by toString(). - * - * @var string - */ - protected $message; - - /** - * Initialises with the expected value and the actual value. - * - * @param mixed $expected expected value retrieved - * @param mixed $actual actual value retrieved - * @param string $expectedAsString - * @param string $actualAsString - * @param bool $identical - * @param string $message a string which is prefixed on all returned lines - * in the difference output - */ - public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '') - { - $this->expected = $expected; - $this->actual = $actual; - $this->expectedAsString = $expectedAsString; - $this->actualAsString = $actualAsString; - $this->message = $message; - } - - public function getActual() - { - return $this->actual; - } - - public function getExpected() - { - return $this->expected; - } - - /** - * @return string - */ - public function getActualAsString() - { - return $this->actualAsString; - } - - /** - * @return string - */ - public function getExpectedAsString() - { - return $this->expectedAsString; - } - - /** - * @return string - */ - public function getDiff() - { - if (!$this->actualAsString && !$this->expectedAsString) { - return ''; - } - - $differ = new Differ(new UnifiedDiffOutputBuilder("\n--- Expected\n+++ Actual\n")); - - return $differ->diff($this->expectedAsString, $this->actualAsString); - } - - /** - * @return string - */ - public function toString() - { - return $this->message . $this->getDiff(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares resources for equality. - */ -class ResourceComparator extends Comparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return \is_resource($expected) && \is_resource($actual); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - if ($actual != $expected) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Factory for comparators which compare values for equality. - */ -class Factory -{ - /** - * @var Factory - */ - private static $instance; - - /** - * @var Comparator[] - */ - private $customComparators = []; - - /** - * @var Comparator[] - */ - private $defaultComparators = []; - - /** - * @return Factory - */ - public static function getInstance() - { - if (self::$instance === null) { - self::$instance = new self; - } - - return self::$instance; - } - - /** - * Constructs a new factory. - */ - public function __construct() - { - $this->registerDefaultComparators(); - } - - /** - * Returns the correct comparator for comparing two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return Comparator - */ - public function getComparatorFor($expected, $actual) - { - foreach ($this->customComparators as $comparator) { - if ($comparator->accepts($expected, $actual)) { - return $comparator; - } - } - - foreach ($this->defaultComparators as $comparator) { - if ($comparator->accepts($expected, $actual)) { - return $comparator; - } - } - } - - /** - * Registers a new comparator. - * - * This comparator will be returned by getComparatorFor() if its accept() method - * returns TRUE for the compared values. It has higher priority than the - * existing comparators, meaning that its accept() method will be invoked - * before those of the other comparators. - * - * @param Comparator $comparator The comparator to be registered - */ - public function register(Comparator $comparator) - { - \array_unshift($this->customComparators, $comparator); - - $comparator->setFactory($this); - } - - /** - * Unregisters a comparator. - * - * This comparator will no longer be considered by getComparatorFor(). - * - * @param Comparator $comparator The comparator to be unregistered - */ - public function unregister(Comparator $comparator) - { - foreach ($this->customComparators as $key => $_comparator) { - if ($comparator === $_comparator) { - unset($this->customComparators[$key]); - } - } - } - - /** - * Unregisters all non-default comparators. - */ - public function reset() - { - $this->customComparators = []; - } - - private function registerDefaultComparators() - { - $this->registerDefaultComparator(new MockObjectComparator); - $this->registerDefaultComparator(new DateTimeComparator); - $this->registerDefaultComparator(new DOMNodeComparator); - $this->registerDefaultComparator(new SplObjectStorageComparator); - $this->registerDefaultComparator(new ExceptionComparator); - $this->registerDefaultComparator(new ObjectComparator); - $this->registerDefaultComparator(new ResourceComparator); - $this->registerDefaultComparator(new ArrayComparator); - $this->registerDefaultComparator(new DoubleComparator); - $this->registerDefaultComparator(new NumericComparator); - $this->registerDefaultComparator(new ScalarComparator); - $this->registerDefaultComparator(new TypeComparator); - } - - private function registerDefaultComparator(Comparator $comparator) - { - $this->defaultComparators[] = $comparator; - - $comparator->setFactory($this); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -use DOMDocument; -use DOMNode; - -/** - * Compares DOMNode instances for equality. - */ -class DOMNodeComparator extends ObjectComparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return $expected instanceof DOMNode && $actual instanceof DOMNode; - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) - { - $expectedAsString = $this->nodeToText($expected, true, $ignoreCase); - $actualAsString = $this->nodeToText($actual, true, $ignoreCase); - - if ($expectedAsString !== $actualAsString) { - $type = $expected instanceof DOMDocument ? 'documents' : 'nodes'; - - throw new ComparisonFailure( - $expected, - $actual, - $expectedAsString, - $actualAsString, - false, - \sprintf("Failed asserting that two DOM %s are equal.\n", $type) - ); - } - } - - /** - * Returns the normalized, whitespace-cleaned, and indented textual - * representation of a DOMNode. - */ - private function nodeToText(DOMNode $node, bool $canonicalize, bool $ignoreCase): string - { - if ($canonicalize) { - $document = new DOMDocument; - @$document->loadXML($node->C14N()); - - $node = $document; - } - - $document = $node instanceof DOMDocument ? $node : $node->ownerDocument; - - $document->formatOutput = true; - $document->normalizeDocument(); - - $text = $node instanceof DOMDocument ? $node->saveXML() : $document->saveXML($node); - - return $ignoreCase ? \strtolower($text) : $text; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares \SplObjectStorage instances for equality. - */ -class SplObjectStorageComparator extends Comparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - foreach ($actual as $object) { - if (!$expected->contains($object)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting that two objects are equal.' - ); - } - } - - foreach ($expected as $object) { - if (!$actual->contains($object)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting that two objects are equal.' - ); - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares objects for equality. - */ -class ObjectComparator extends ArrayComparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return \is_object($expected) && \is_object($actual); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) - { - if (\get_class($actual) !== \get_class($expected)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - \sprintf( - '%s is not instance of expected class "%s".', - $this->exporter->export($actual), - \get_class($expected) - ) - ); - } - - // don't compare twice to allow for cyclic dependencies - if (\in_array([$actual, $expected], $processed, true) || - \in_array([$expected, $actual], $processed, true)) { - return; - } - - $processed[] = [$actual, $expected]; - - // don't compare objects if they are identical - // this helps to avoid the error "maximum function nesting level reached" - // CAUTION: this conditional clause is not tested - if ($actual !== $expected) { - try { - parent::assertEquals( - $this->toArray($expected), - $this->toArray($actual), - $delta, - $canonicalize, - $ignoreCase, - $processed - ); - } catch (ComparisonFailure $e) { - throw new ComparisonFailure( - $expected, - $actual, - // replace "Array" with "MyClass object" - \substr_replace($e->getExpectedAsString(), \get_class($expected) . ' Object', 0, 5), - \substr_replace($e->getActualAsString(), \get_class($actual) . ' Object', 0, 5), - false, - 'Failed asserting that two objects are equal.' - ); - } - } - } - - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param object $object - * - * @return array - */ - protected function toArray($object) - { - return $this->exporter->toArray($object); - } -} -Comparator - -Copyright (c) 2002-2018, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares DateTimeInterface instances for equality. - */ -class DateTimeComparator extends ObjectComparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - */ - public function accepts($expected, $actual) - { - return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && - ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * @param array $processed List of already processed elements (used to prevent infinite recursion) - * - * @throws \Exception - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = []) - { - /** @var \DateTimeInterface $expected */ - /** @var \DateTimeInterface $actual */ - $absDelta = \abs($delta); - $delta = new \DateInterval(\sprintf('PT%dS', $absDelta)); - $delta->f = $absDelta - \floor($absDelta); - - $actualClone = (clone $actual) - ->setTimezone(new \DateTimeZone('UTC')); - - $expectedLower = (clone $expected) - ->setTimezone(new \DateTimeZone('UTC')) - ->sub($delta); - - $expectedUpper = (clone $expected) - ->setTimezone(new \DateTimeZone('UTC')) - ->add($delta); - - if ($actualClone < $expectedLower || $actualClone > $expectedUpper) { - throw new ComparisonFailure( - $expected, - $actual, - $this->dateTimeToString($expected), - $this->dateTimeToString($actual), - false, - 'Failed asserting that two DateTime objects are equal.' - ); - } - } - - /** - * Returns an ISO 8601 formatted string representation of a datetime or - * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly - * initialized. - */ - private function dateTimeToString(\DateTimeInterface $datetime): string - { - $string = $datetime->format('Y-m-d\TH:i:s.uO'); - - return $string ?: 'Invalid DateTimeInterface object'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Comparator; - -/** - * Compares scalar or NULL values for equality. - */ -class ScalarComparator extends Comparator -{ - /** - * Returns whether the comparator can compare two values. - * - * @param mixed $expected The first value to compare - * @param mixed $actual The second value to compare - * - * @return bool - * - * @since Method available since Release 3.6.0 - */ - public function accepts($expected, $actual) - { - return ((\is_scalar($expected) xor null === $expected) && - (\is_scalar($actual) xor null === $actual)) - // allow comparison between strings and objects featuring __toString() - || (\is_string($expected) && \is_object($actual) && \method_exists($actual, '__toString')) - || (\is_object($expected) && \method_exists($expected, '__toString') && \is_string($actual)); - } - - /** - * Asserts that two values are equal. - * - * @param mixed $expected First value to compare - * @param mixed $actual Second value to compare - * @param float $delta Allowed numerical distance between two values to consider them equal - * @param bool $canonicalize Arrays are sorted before comparison when set to true - * @param bool $ignoreCase Case is ignored when set to true - * - * @throws ComparisonFailure - */ - public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) - { - $expectedToCompare = $expected; - $actualToCompare = $actual; - - // always compare as strings to avoid strange behaviour - // otherwise 0 == 'Foobar' - if (\is_string($expected) || \is_string($actual)) { - $expectedToCompare = (string) $expectedToCompare; - $actualToCompare = (string) $actualToCompare; - - if ($ignoreCase) { - $expectedToCompare = \strtolower($expectedToCompare); - $actualToCompare = \strtolower($actualToCompare); - } - } - - if ($expectedToCompare !== $actualToCompare && \is_string($expected) && \is_string($actual)) { - throw new ComparisonFailure( - $expected, - $actual, - $this->exporter->export($expected), - $this->exporter->export($actual), - false, - 'Failed asserting that two strings are equal.' - ); - } - - if ($expectedToCompare != $actualToCompare) { - throw new ComparisonFailure( - $expected, - $actual, - // no diff is required - '', - '', - false, - \sprintf( - 'Failed asserting that %s matches expected %s.', - $this->exporter->export($actual), - $this->exporter->export($expected) - ) - ); - } - } -} -line = $line; - $this->name = $name; - $this->value = $value; - } - - /** - * @return int - */ - public function getLine(): int { - return $this->line; - } - - /** - * @return string - */ - public function getName(): string { - return $this->name; - } - - /** - * @return string - */ - public function getValue(): string { - return $this->value; - } - -} -xmlns = $xmlns; - } - - /** - * @param TokenCollection $tokens - * - * @return DOMDocument - */ - public function toDom(TokenCollection $tokens): DOMDocument { - $dom = new DOMDocument(); - $dom->preserveWhiteSpace = false; - $dom->loadXML($this->toXML($tokens)); - - return $dom; - } - - /** - * @param TokenCollection $tokens - * - * @return string - */ - public function toXML(TokenCollection $tokens): string { - $this->writer = new \XMLWriter(); - $this->writer->openMemory(); - $this->writer->setIndent(true); - $this->writer->startDocument(); - $this->writer->startElement('source'); - $this->writer->writeAttribute('xmlns', $this->xmlns->asString()); - $this->writer->startElement('line'); - $this->writer->writeAttribute('no', '1'); - - $this->previousToken = $tokens[0]; - foreach ($tokens as $token) { - $this->addToken($token); - } - - $this->writer->endElement(); - $this->writer->endElement(); - $this->writer->endDocument(); - - return $this->writer->outputMemory(); - } - - /** - * @param Token $token - */ - private function addToken(Token $token) { - if ($this->previousToken->getLine() < $token->getLine()) { - $this->writer->endElement(); - - $this->writer->startElement('line'); - $this->writer->writeAttribute('no', (string)$token->getLine()); - $this->previousToken = $token; - } - - if ($token->getValue() !== '') { - $this->writer->startElement('token'); - $this->writer->writeAttribute('name', $token->getName()); - $this->writer->writeRaw(htmlspecialchars($token->getValue(), ENT_NOQUOTES | ENT_DISALLOWED | ENT_XML1)); - $this->writer->endElement(); - } - } -} -tokens[] = $token; - } - - /** - * @return Token - */ - public function current(): Token { - return current($this->tokens); - } - - /** - * @return int - */ - public function key(): int { - return key($this->tokens); - } - - /** - * @return void - */ - public function next() { - next($this->tokens); - $this->pos++; - } - - /** - * @return bool - */ - public function valid(): bool { - return $this->count() > $this->pos; - } - - /** - * @return void - */ - public function rewind() { - reset($this->tokens); - $this->pos = 0; - } - - /** - * @return int - */ - public function count(): int { - return count($this->tokens); - } - - /** - * @param mixed $offset - * - * @return bool - */ - public function offsetExists($offset): bool { - return isset($this->tokens[$offset]); - } - - /** - * @param mixed $offset - * - * @return Token - * @throws TokenCollectionException - */ - public function offsetGet($offset): Token { - if (!$this->offsetExists($offset)) { - throw new TokenCollectionException( - sprintf('No Token at offest %s', $offset) - ); - } - - return $this->tokens[$offset]; - } - - /** - * @param mixed $offset - * @param Token $value - * - * @throws TokenCollectionException - */ - public function offsetSet($offset, $value) { - if (!is_int($offset)) { - $type = gettype($offset); - throw new TokenCollectionException( - sprintf( - 'Offset must be of type integer, %s given', - $type === 'object' ? get_class($value) : $type - ) - ); - } - if (!$value instanceof Token) { - $type = gettype($value); - throw new TokenCollectionException( - sprintf( - 'Value must be of type %s, %s given', - Token::class, - $type === 'object' ? get_class($value) : $type - ) - ); - } - $this->tokens[$offset] = $value; - } - - /** - * @param mixed $offset - */ - public function offsetUnset($offset) { - unset($this->tokens[$offset]); - } - -} - 'T_OPEN_BRACKET', - ')' => 'T_CLOSE_BRACKET', - '[' => 'T_OPEN_SQUARE', - ']' => 'T_CLOSE_SQUARE', - '{' => 'T_OPEN_CURLY', - '}' => 'T_CLOSE_CURLY', - ';' => 'T_SEMICOLON', - '.' => 'T_DOT', - ',' => 'T_COMMA', - '=' => 'T_EQUAL', - '<' => 'T_LT', - '>' => 'T_GT', - '+' => 'T_PLUS', - '-' => 'T_MINUS', - '*' => 'T_MULT', - '/' => 'T_DIV', - '?' => 'T_QUESTION_MARK', - '!' => 'T_EXCLAMATION_MARK', - ':' => 'T_COLON', - '"' => 'T_DOUBLE_QUOTES', - '@' => 'T_AT', - '&' => 'T_AMPERSAND', - '%' => 'T_PERCENT', - '|' => 'T_PIPE', - '$' => 'T_DOLLAR', - '^' => 'T_CARET', - '~' => 'T_TILDE', - '`' => 'T_BACKTICK' - ]; - - public function parse(string $source): TokenCollection { - $result = new TokenCollection(); - $tokens = token_get_all($source); - - $lastToken = new Token( - $tokens[0][2], - 'Placeholder', - '' - ); - - foreach ($tokens as $pos => $tok) { - if (is_string($tok)) { - $token = new Token( - $lastToken->getLine(), - $this->map[$tok], - $tok - ); - $result->addToken($token); - $lastToken = $token; - continue; - } - - $line = $tok[2]; - $values = preg_split('/\R+/Uu', $tok[1]); - - foreach ($values as $v) { - $token = new Token( - $line, - token_name($tok[0]), - $v - ); - $result->addToken($token); - $line++; - $lastToken = $token; - } - } - - return $result; - } - -} -Tokenizer - -Copyright (c) 2017 Arne Blankerts and contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -ensureValidUri($value); - $this->value = $value; - } - - public function asString(): string { - return $this->value; - } - - private function ensureValidUri($value) { - if (strpos($value, ':') === false) { - throw new NamespaceUriException( - sprintf("Namespace URI '%s' must contain at least one colon", $value) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\RecursionContext; - -/** - * A context containing previously processed arrays and objects - * when recursively processing a value. - */ -final class Context -{ - /** - * @var array[] - */ - private $arrays; - - /** - * @var \SplObjectStorage - */ - private $objects; - - /** - * Initialises the context - */ - public function __construct() - { - $this->arrays = array(); - $this->objects = new \SplObjectStorage; - } - - /** - * Adds a value to the context. - * - * @param array|object $value The value to add. - * - * @return int|string The ID of the stored value, either as a string or integer. - * - * @throws InvalidArgumentException Thrown if $value is not an array or object - */ - public function add(&$value) - { - if (is_array($value)) { - return $this->addArray($value); - } elseif (is_object($value)) { - return $this->addObject($value); - } - - throw new InvalidArgumentException( - 'Only arrays and objects are supported' - ); - } - - /** - * Checks if the given value exists within the context. - * - * @param array|object $value The value to check. - * - * @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored. - * - * @throws InvalidArgumentException Thrown if $value is not an array or object - */ - public function contains(&$value) - { - if (is_array($value)) { - return $this->containsArray($value); - } elseif (is_object($value)) { - return $this->containsObject($value); - } - - throw new InvalidArgumentException( - 'Only arrays and objects are supported' - ); - } - - /** - * @param array $array - * - * @return bool|int - */ - private function addArray(array &$array) - { - $key = $this->containsArray($array); - - if ($key !== false) { - return $key; - } - - $key = count($this->arrays); - $this->arrays[] = &$array; - - if (!isset($array[PHP_INT_MAX]) && !isset($array[PHP_INT_MAX - 1])) { - $array[] = $key; - $array[] = $this->objects; - } else { /* cover the improbable case too */ - do { - $key = random_int(PHP_INT_MIN, PHP_INT_MAX); - } while (isset($array[$key])); - - $array[$key] = $key; - - do { - $key = random_int(PHP_INT_MIN, PHP_INT_MAX); - } while (isset($array[$key])); - - $array[$key] = $this->objects; - } - - return $key; - } - - /** - * @param object $object - * - * @return string - */ - private function addObject($object) - { - if (!$this->objects->contains($object)) { - $this->objects->attach($object); - } - - return spl_object_hash($object); - } - - /** - * @param array $array - * - * @return int|false - */ - private function containsArray(array &$array) - { - $end = array_slice($array, -2); - - return isset($end[1]) && $end[1] === $this->objects ? $end[0] : false; - } - - /** - * @param object $value - * - * @return string|false - */ - private function containsObject($value) - { - if ($this->objects->contains($value)) { - return spl_object_hash($value); - } - - return false; - } - - public function __destruct() - { - foreach ($this->arrays as &$array) { - if (is_array($array)) { - array_pop($array); - array_pop($array); - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\RecursionContext; - -/** - */ -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\RecursionContext; - -/** - */ -final class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} -Recursion Context - -Copyright (c) 2002-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * A simple template engine. - * - * @since Class available since Release 1.0.0 - */ -class Text_Template -{ - /** - * @var string - */ - protected $template = ''; - - /** - * @var string - */ - protected $openDelimiter = '{'; - - /** - * @var string - */ - protected $closeDelimiter = '}'; - - /** - * @var array - */ - protected $values = array(); - - /** - * Constructor. - * - * @param string $file - * @throws InvalidArgumentException - */ - public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}') - { - $this->setFile($file); - $this->openDelimiter = $openDelimiter; - $this->closeDelimiter = $closeDelimiter; - } - - /** - * Sets the template file. - * - * @param string $file - * @throws InvalidArgumentException - */ - public function setFile($file) - { - $distFile = $file . '.dist'; - - if (file_exists($file)) { - $this->template = file_get_contents($file); - } - - else if (file_exists($distFile)) { - $this->template = file_get_contents($distFile); - } - - else { - throw new InvalidArgumentException( - 'Template file could not be loaded.' - ); - } - } - - /** - * Sets one or more template variables. - * - * @param array $values - * @param bool $merge - */ - public function setVar(array $values, $merge = TRUE) - { - if (!$merge || empty($this->values)) { - $this->values = $values; - } else { - $this->values = array_merge($this->values, $values); - } - } - - /** - * Renders the template and returns the result. - * - * @return string - */ - public function render() - { - $keys = array(); - - foreach ($this->values as $key => $value) { - $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; - } - - return str_replace($keys, $this->values, $this->template); - } - - /** - * Renders the template and writes the result to a file. - * - * @param string $target - */ - public function renderTo($target) - { - $fp = @fopen($target, 'wt'); - - if ($fp) { - fwrite($fp, $this->render()); - fclose($fp); - } else { - $error = error_get_last(); - - throw new RuntimeException( - sprintf( - 'Could not write to %s: %s', - $target, - substr( - $error['message'], - strpos($error['message'], ':') + 2 - ) - ) - ); - } - } -} - -Text_Template - -Copyright (c) 2009-2015, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -Object Enumerator - -Copyright (c) 2016-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use SebastianBergmann\Version as VersionId; - -/** - * This class defines the current version of PHPUnit. - */ -class Version -{ - private static $pharVersion = "7.5.6"; - - private static $version; - - /** - * Returns the current version of PHPUnit. - */ - public static function id(): string - { - if (self::$pharVersion !== null) { - return self::$pharVersion; - } - - if (self::$version === null) { - $version = new VersionId('7.5.6', \dirname(__DIR__, 2)); - self::$version = $version->getVersion(); - } - - return self::$version; - } - - public static function series(): string - { - if (\strpos(self::id(), '-')) { - $version = \explode('-', self::id())[0]; - } else { - $version = self::id(); - } - - return \implode('.', \array_slice(\explode('.', $version), 0, 2)); - } - - public static function getVersionString(): string - { - return 'PHPUnit ' . self::id() . ' by Sebastian Bergmann and contributors.'; - } - - public static function getReleaseChannel(): string - { - if (\strpos(self::$pharVersion, '-') !== false) { - return '-nightly'; - } - - return ''; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use PHPUnit\Framework\DataProviderTestSuite; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; - -final class TestSuiteSorter -{ - /** - * @var int - */ - public const ORDER_DEFAULT = 0; - - /** - * @var int - */ - public const ORDER_RANDOMIZED = 1; - - /** - * @var int - */ - public const ORDER_REVERSED = 2; - - /** - * @var int - */ - public const ORDER_DEFECTS_FIRST = 3; - - /** - * @var int - */ - public const ORDER_DURATION = 4; - - /** - * List of sorting weights for all test result codes. A higher number gives higher priority. - */ - private const DEFECT_SORT_WEIGHT = [ - BaseTestRunner::STATUS_ERROR => 6, - BaseTestRunner::STATUS_FAILURE => 5, - BaseTestRunner::STATUS_WARNING => 4, - BaseTestRunner::STATUS_INCOMPLETE => 3, - BaseTestRunner::STATUS_RISKY => 2, - BaseTestRunner::STATUS_SKIPPED => 1, - BaseTestRunner::STATUS_UNKNOWN => 0, - ]; - - /** - * @var array Associative array of (string => DEFECT_SORT_WEIGHT) elements - */ - private $defectSortOrder = []; - - /** - * @var TestResultCacheInterface - */ - private $cache; - - /** - * @var array array A list of normalized names of tests before reordering - */ - private $originalExecutionOrder = []; - - /** - * @var array array A list of normalized names of tests affected by reordering - */ - private $executionOrder = []; - - public static function getTestSorterUID(Test $test): string - { - if ($test instanceof PhptTestCase) { - return $test->getName(); - } - - if ($test instanceof TestCase) { - $testName = $test->getName(true); - - if (\strpos($testName, '::') === false) { - $testName = \get_class($test) . '::' . $testName; - } - - return $testName; - } - - return $test->getName(); - } - - public function __construct(?TestResultCacheInterface $cache = null) - { - $this->cache = $cache ?? new NullTestResultCache; - } - - /** - * @throws Exception - */ - public function reorderTestsInSuite(Test $suite, int $order, bool $resolveDependencies, int $orderDefects, bool $isRootTestSuite = true): void - { - $allowedOrders = [ - self::ORDER_DEFAULT, - self::ORDER_REVERSED, - self::ORDER_RANDOMIZED, - self::ORDER_DURATION, - ]; - - if (!\in_array($order, $allowedOrders, true)) { - throw new Exception( - '$order must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_REVERSED, or TestSuiteSorter::ORDER_RANDOMIZED, or TestSuiteSorter::ORDER_DURATION' - ); - } - - $allowedOrderDefects = [ - self::ORDER_DEFAULT, - self::ORDER_DEFECTS_FIRST, - ]; - - if (!\in_array($orderDefects, $allowedOrderDefects, true)) { - throw new Exception( - '$orderDefects must be one of TestSuiteSorter::ORDER_DEFAULT, TestSuiteSorter::ORDER_DEFECTS_FIRST' - ); - } - - if ($isRootTestSuite) { - $this->originalExecutionOrder = $this->calculateTestExecutionOrder($suite); - } - - if ($suite instanceof TestSuite) { - foreach ($suite as $_suite) { - $this->reorderTestsInSuite($_suite, $order, $resolveDependencies, $orderDefects, false); - } - - if ($orderDefects === self::ORDER_DEFECTS_FIRST) { - $this->addSuiteToDefectSortOrder($suite); - } - - $this->sort($suite, $order, $resolveDependencies, $orderDefects); - } - - if ($isRootTestSuite) { - $this->executionOrder = $this->calculateTestExecutionOrder($suite); - } - } - - public function getOriginalExecutionOrder(): array - { - return $this->originalExecutionOrder; - } - - public function getExecutionOrder(): array - { - return $this->executionOrder; - } - - private function sort(TestSuite $suite, int $order, bool $resolveDependencies, int $orderDefects): void - { - if (empty($suite->tests())) { - return; - } - - if ($order === self::ORDER_REVERSED) { - $suite->setTests($this->reverse($suite->tests())); - } elseif ($order === self::ORDER_RANDOMIZED) { - $suite->setTests($this->randomize($suite->tests())); - } elseif ($order === self::ORDER_DURATION && $this->cache !== null) { - $suite->setTests($this->sortByDuration($suite->tests())); - } - - if ($orderDefects === self::ORDER_DEFECTS_FIRST && $this->cache !== null) { - $suite->setTests($this->sortDefectsFirst($suite->tests())); - } - - if ($resolveDependencies && !($suite instanceof DataProviderTestSuite) && $this->suiteOnlyContainsTests($suite)) { - $suite->setTests($this->resolveDependencies($suite->tests())); - } - } - - private function addSuiteToDefectSortOrder(TestSuite $suite): void - { - $max = 0; - - foreach ($suite->tests() as $test) { - $testname = self::getTestSorterUID($test); - - if (!isset($this->defectSortOrder[$testname])) { - $this->defectSortOrder[$testname] = self::DEFECT_SORT_WEIGHT[$this->cache->getState($testname)]; - $max = \max($max, $this->defectSortOrder[$testname]); - } - } - - $this->defectSortOrder[$suite->getName()] = $max; - } - - private function suiteOnlyContainsTests(TestSuite $suite): bool - { - return \array_reduce( - $suite->tests(), - function ($carry, $test) { - return $carry && ($test instanceof TestCase || $test instanceof DataProviderTestSuite); - }, - true - ); - } - - private function reverse(array $tests): array - { - return \array_reverse($tests); - } - - private function randomize(array $tests): array - { - \shuffle($tests); - - return $tests; - } - - private function sortDefectsFirst(array $tests): array - { - \usort( - $tests, - function ($left, $right) { - return $this->cmpDefectPriorityAndTime($left, $right); - } - ); - - return $tests; - } - - private function sortByDuration(array $tests): array - { - \usort( - $tests, - function ($left, $right) { - return $this->cmpDuration($left, $right); - } - ); - - return $tests; - } - - /** - * Comparator callback function to sort tests for "reach failure as fast as possible": - * 1. sort tests by defect weight defined in self::DEFECT_SORT_WEIGHT - * 2. when tests are equally defective, sort the fastest to the front - * 3. do not reorder successful tests - */ - private function cmpDefectPriorityAndTime(Test $a, Test $b): int - { - $priorityA = $this->defectSortOrder[self::getTestSorterUID($a)] ?? 0; - $priorityB = $this->defectSortOrder[self::getTestSorterUID($b)] ?? 0; - - if ($priorityB <=> $priorityA) { - // Sort defect weight descending - return $priorityB <=> $priorityA; - } - - if ($priorityA || $priorityB) { - return $this->cmpDuration($a, $b); - } - - // do not change execution order - return 0; - } - - /** - * Compares test duration for sorting tests by duration ascending. - */ - private function cmpDuration(Test $a, Test $b): int - { - return $this->cache->getTime(self::getTestSorterUID($a)) <=> $this->cache->getTime(self::getTestSorterUID($b)); - } - - /** - * Reorder Tests within a TestCase in such a way as to resolve as many dependencies as possible. - * The algorithm will leave the tests in original running order when it can. - * For more details see the documentation for test dependencies. - * - * Short description of algorithm: - * 1. Pick the next Test from remaining tests to be checked for dependencies. - * 2. If the test has no dependencies: mark done, start again from the top - * 3. If the test has dependencies but none left to do: mark done, start again from the top - * 4. When we reach the end add any leftover tests to the end. These will be marked 'skipped' during execution. - * - * @param array $tests - * - * @return array - */ - private function resolveDependencies(array $tests): array - { - $newTestOrder = []; - $i = 0; - - do { - $todoNames = \array_map( - function ($test) { - return self::getTestSorterUID($test); - }, - $tests - ); - - if (!$tests[$i]->hasDependencies() || empty(\array_intersect($this->getNormalizedDependencyNames($tests[$i]), $todoNames))) { - $newTestOrder = \array_merge($newTestOrder, \array_splice($tests, $i, 1)); - $i = 0; - } else { - $i++; - } - } while (!empty($tests) && ($i < \count($tests))); - - return \array_merge($newTestOrder, $tests); - } - - /** - * @param DataProviderTestSuite|TestCase $test - * - * @return array A list of full test names as "TestSuiteClassName::testMethodName" - */ - private function getNormalizedDependencyNames($test): array - { - if ($test instanceof DataProviderTestSuite) { - $testClass = \substr($test->getName(), 0, \strpos($test->getName(), '::')); - } else { - $testClass = \get_class($test); - } - - $names = \array_map( - function ($name) use ($testClass) { - return \strpos($name, '::') === false ? $testClass . '::' . $name : $name; - }, - $test->getDependencies() - ); - - return $names; - } - - private function calculateTestExecutionOrder(Test $suite): array - { - $tests = []; - - if ($suite instanceof TestSuite) { - foreach ($suite->tests() as $test) { - if (!($test instanceof TestSuite)) { - $tests[] = self::getTestSorterUID($test); - } else { - $tests = \array_merge($tests, $this->calculateTestExecutionOrder($test)); - } - } - } - - return $tests; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use PHPUnit\Framework\TestCase; -use PHPUnit\Util\FileLoader; -use PHPUnit\Util\Filesystem; -use ReflectionClass; - -/** - * The standard test suite loader. - */ -class StandardTestSuiteLoader implements TestSuiteLoader -{ - /** - * @throws Exception - * @throws \PHPUnit\Framework\Exception - */ - public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass - { - $suiteClassName = \str_replace('.php', '', $suiteClassName); - - if (empty($suiteClassFile)) { - $suiteClassFile = Filesystem::classNameToFilename( - $suiteClassName - ); - } - - if (!\class_exists($suiteClassName, false)) { - $loadedClasses = \get_declared_classes(); - - $filename = FileLoader::checkAndLoad($suiteClassFile); - - $loadedClasses = \array_values( - \array_diff(\get_declared_classes(), $loadedClasses) - ); - } - - if (!\class_exists($suiteClassName, false) && !empty($loadedClasses)) { - $offset = 0 - \strlen($suiteClassName); - - foreach ($loadedClasses as $loadedClass) { - $class = new ReflectionClass($loadedClass); - - if (\substr($loadedClass, $offset) === $suiteClassName && - $class->getFileName() == $filename) { - $suiteClassName = $loadedClass; - - break; - } - } - } - - if (!\class_exists($suiteClassName, false) && !empty($loadedClasses)) { - $testCaseClass = TestCase::class; - - foreach ($loadedClasses as $loadedClass) { - $class = new ReflectionClass($loadedClass); - $classFile = $class->getFileName(); - - if ($class->isSubclassOf($testCaseClass) && !$class->isAbstract()) { - $suiteClassName = $loadedClass; - $testCaseClass = $loadedClass; - - if ($classFile == \realpath($suiteClassFile)) { - break; - } - } - - if ($class->hasMethod('suite')) { - $method = $class->getMethod('suite'); - - if (!$method->isAbstract() && $method->isPublic() && $method->isStatic()) { - $suiteClassName = $loadedClass; - - if ($classFile == \realpath($suiteClassFile)) { - break; - } - } - } - } - } - - if (\class_exists($suiteClassName, false)) { - $class = new ReflectionClass($suiteClassName); - - if ($class->getFileName() == \realpath($suiteClassFile)) { - return $class; - } - } - - throw new Exception( - \sprintf( - "Class '%s' could not be found in '%s'.", - $suiteClassName, - $suiteClassFile - ) - ); - } - - public function reload(ReflectionClass $aClass): ReflectionClass - { - return $aClass; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -final class ResultCacheExtension implements AfterSuccessfulTestHook, AfterSkippedTestHook, AfterRiskyTestHook, AfterIncompleteTestHook, AfterTestErrorHook, AfterTestWarningHook, AfterTestFailureHook, AfterLastTestHook -{ - /** - * @var TestResultCacheInterface - */ - private $cache; - - public function __construct(TestResultCache $cache) - { - $this->cache = $cache; - } - - public function flush(): void - { - $this->cache->persist(); - } - - public function executeAfterSuccessfulTest(string $test, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - } - - public function executeAfterIncompleteTest(string $test, string $message, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, BaseTestRunner::STATUS_INCOMPLETE); - } - - public function executeAfterRiskyTest(string $test, string $message, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, BaseTestRunner::STATUS_RISKY); - } - - public function executeAfterSkippedTest(string $test, string $message, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, BaseTestRunner::STATUS_SKIPPED); - } - - public function executeAfterTestError(string $test, string $message, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, BaseTestRunner::STATUS_ERROR); - } - - public function executeAfterTestFailure(string $test, string $message, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, BaseTestRunner::STATUS_FAILURE); - } - - public function executeAfterTestWarning(string $test, string $message, float $time): void - { - $testName = $this->getTestName($test); - - $this->cache->setTime($testName, \round($time, 3)); - $this->cache->setState($testName, BaseTestRunner::STATUS_WARNING); - } - - public function executeAfterLastTest(): void - { - $this->flush(); - } - - /** - * @param string $test A long description format of the current test - * - * @return string The test name without TestSuiteClassName:: and @dataprovider details - */ - private function getTestName(string $test): string - { - $matches = []; - - if (\preg_match('/^(?\S+::\S+)(?:(? with data set (?:#\d+|"[^"]+"))\s\()?/', $test, $matches)) { - $test = $matches['name'] . ($matches['dataname'] ?? ''); - } - - return $test; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface BeforeTestHook extends TestHook -{ - public function executeBeforeTest(string $test): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Util\Test as TestUtil; - -final class TestListenerAdapter implements TestListener -{ - /** - * @var TestHook[] - */ - private $hooks = []; - - /** - * @var bool - */ - private $lastTestWasNotSuccessful; - - public function add(TestHook $hook): void - { - $this->hooks[] = $hook; - } - - public function startTest(Test $test): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof BeforeTestHook) { - $hook->executeBeforeTest(TestUtil::describeAsString($test)); - } - } - - $this->lastTestWasNotSuccessful = false; - } - - public function addError(Test $test, \Throwable $t, float $time): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterTestErrorHook) { - $hook->executeAfterTestError(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - - $this->lastTestWasNotSuccessful = true; - } - - public function addWarning(Test $test, Warning $e, float $time): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterTestWarningHook) { - $hook->executeAfterTestWarning(TestUtil::describeAsString($test), $e->getMessage(), $time); - } - } - - $this->lastTestWasNotSuccessful = true; - } - - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterTestFailureHook) { - $hook->executeAfterTestFailure(TestUtil::describeAsString($test), $e->getMessage(), $time); - } - } - - $this->lastTestWasNotSuccessful = true; - } - - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterIncompleteTestHook) { - $hook->executeAfterIncompleteTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - - $this->lastTestWasNotSuccessful = true; - } - - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterRiskyTestHook) { - $hook->executeAfterRiskyTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - - $this->lastTestWasNotSuccessful = true; - } - - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterSkippedTestHook) { - $hook->executeAfterSkippedTest(TestUtil::describeAsString($test), $t->getMessage(), $time); - } - } - - $this->lastTestWasNotSuccessful = true; - } - - public function endTest(Test $test, float $time): void - { - if ($this->lastTestWasNotSuccessful !== true) { - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterSuccessfulTestHook) { - $hook->executeAfterSuccessfulTest(TestUtil::describeAsString($test), $time); - } - } - } - - foreach ($this->hooks as $hook) { - if ($hook instanceof AfterTestHook) { - $hook->executeAfterTest(TestUtil::describeAsString($test), $time); - } - } - } - - public function startTestSuite(TestSuite $suite): void - { - } - - public function endTestSuite(TestSuite $suite): void - { - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterTestErrorHook extends TestHook -{ - public function executeAfterTestError(string $test, string $message, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterTestFailureHook extends TestHook -{ - public function executeAfterTestFailure(string $test, string $message, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterLastTestHook extends Hook -{ - public function executeAfterLastTest(): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface BeforeFirstTestHook extends Hook -{ - public function executeBeforeFirstTest(): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface Hook -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterRiskyTestHook extends TestHook -{ - public function executeAfterRiskyTest(string $test, string $message, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterSuccessfulTestHook extends TestHook -{ - public function executeAfterSuccessfulTest(string $test, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterTestWarningHook extends TestHook -{ - public function executeAfterTestWarning(string $test, string $message, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterSkippedTestHook extends TestHook -{ - public function executeAfterSkippedTest(string $test, string $message, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterIncompleteTestHook extends TestHook -{ - public function executeAfterIncompleteTest(string $test, string $message, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface TestHook extends Hook -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface AfterTestHook extends Hook -{ - /** - * This hook will fire after any test, regardless of the result. - * - * For more fine grained control, have a look at the other hooks - * that extend PHPUnit\Runner\Hook. - */ - public function executeAfterTest(string $test, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -class Exception extends \RuntimeException implements \PHPUnit\Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use ReflectionClass; - -/** - * An interface to define how a test suite should be loaded. - */ -interface TestSuiteLoader -{ - public function load(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass; - - public function reload(ReflectionClass $aClass): ReflectionClass; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\IncompleteTestError; -use PHPUnit\Framework\SelfDescribing; -use PHPUnit\Framework\SkippedTestError; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestResult; -use PHPUnit\Util\PHP\AbstractPhpProcess; -use SebastianBergmann\Timer\Timer; -use Text_Template; -use Throwable; - -/** - * Runner for PHPT test cases. - */ -class PhptTestCase implements Test, SelfDescribing -{ - /** - * @var string[] - */ - private const SETTINGS = [ - 'allow_url_fopen=1', - 'auto_append_file=', - 'auto_prepend_file=', - 'disable_functions=', - 'display_errors=1', - 'docref_root=', - 'docref_ext=.html', - 'error_append_string=', - 'error_prepend_string=', - 'error_reporting=-1', - 'html_errors=0', - 'log_errors=0', - 'magic_quotes_runtime=0', - 'output_handler=', - 'open_basedir=', - 'output_buffering=Off', - 'report_memleaks=0', - 'report_zend_debug=0', - 'safe_mode=0', - 'xdebug.default_enable=0', - ]; - - /** - * @var string - */ - private $filename; - - /** - * @var AbstractPhpProcess - */ - private $phpUtil; - - /** - * @var string - */ - private $output = ''; - - /** - * Constructs a test case with the given filename. - * - * @throws Exception - */ - public function __construct(string $filename, AbstractPhpProcess $phpUtil = null) - { - if (!\is_file($filename)) { - throw new Exception( - \sprintf( - 'File "%s" does not exist.', - $filename - ) - ); - } - - $this->filename = $filename; - $this->phpUtil = $phpUtil ?: AbstractPhpProcess::factory(); - } - - /** - * Counts the number of test cases executed by run(TestResult result). - */ - public function count(): int - { - return 1; - } - - /** - * Runs a test and collects its result in a TestResult instance. - * - * @throws Exception - * @throws \ReflectionException - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException - * @throws \SebastianBergmann\CodeCoverage\RuntimeException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function run(TestResult $result = null): TestResult - { - if ($result === null) { - $result = new TestResult; - } - - try { - $sections = $this->parse(); - } catch (Exception $e) { - $result->startTest($this); - $result->addFailure($this, new SkippedTestError($e->getMessage()), 0); - $result->endTest($this, 0); - - return $result; - } - - $code = $this->render($sections['FILE']); - $xfail = false; - $settings = $this->parseIniSection(self::SETTINGS); - - $result->startTest($this); - - if (isset($sections['INI'])) { - $settings = $this->parseIniSection($sections['INI'], $settings); - } - - if (isset($sections['ENV'])) { - $env = $this->parseEnvSection($sections['ENV']); - $this->phpUtil->setEnv($env); - } - - $this->phpUtil->setUseStderrRedirection(true); - - if ($result->enforcesTimeLimit()) { - $this->phpUtil->setTimeout($result->getTimeoutForLargeTests()); - } - - $skip = $this->runSkip($sections, $result, $settings); - - if ($skip) { - return $result; - } - - if (isset($sections['XFAIL'])) { - $xfail = \trim($sections['XFAIL']); - } - - if (isset($sections['STDIN'])) { - $this->phpUtil->setStdin($sections['STDIN']); - } - - if (isset($sections['ARGS'])) { - $this->phpUtil->setArgs($sections['ARGS']); - } - - if ($result->getCollectCodeCoverageInformation()) { - $this->renderForCoverage($code); - } - - Timer::start(); - - $jobResult = $this->phpUtil->runJob($code, $this->stringifyIni($settings)); - $time = Timer::stop(); - $this->output = $jobResult['stdout'] ?? ''; - - if ($result->getCollectCodeCoverageInformation() && ($coverage = $this->cleanupForCoverage())) { - $result->getCodeCoverage()->append($coverage, $this, true, [], [], true); - } - - try { - $this->assertPhptExpectation($sections, $jobResult['stdout']); - } catch (AssertionFailedError $e) { - $failure = $e; - - if ($xfail !== false) { - $failure = new IncompleteTestError($xfail, 0, $e); - } - $result->addFailure($this, $failure, $time); - } catch (Throwable $t) { - $result->addError($this, $t, $time); - } - - if ($result->allCompletelyImplemented() && $xfail !== false) { - $result->addFailure($this, new IncompleteTestError('XFAIL section but test passes'), $time); - } - - $this->runClean($sections); - - $result->endTest($this, $time); - - return $result; - } - - /** - * Returns the name of the test case. - */ - public function getName(): string - { - return $this->toString(); - } - - /** - * Returns a string representation of the test case. - */ - public function toString(): string - { - return $this->filename; - } - - public function usesDataProvider(): bool - { - return false; - } - - public function getNumAssertions(): int - { - return 1; - } - - public function getActualOutput(): string - { - return $this->output; - } - - public function hasOutput(): bool - { - return !empty($this->output); - } - - /** - * Parse --INI-- section key value pairs and return as array. - * - * @param array|string - */ - private function parseIniSection($content, $ini = []): array - { - if (\is_string($content)) { - $content = \explode("\n", \trim($content)); - } - - foreach ($content as $setting) { - if (\strpos($setting, '=') === false) { - continue; - } - - $setting = \explode('=', $setting, 2); - $name = \trim($setting[0]); - $value = \trim($setting[1]); - - if ($name === 'extension' || $name === 'zend_extension') { - if (!isset($ini[$name])) { - $ini[$name] = []; - } - - $ini[$name][] = $value; - - continue; - } - - $ini[$name] = $value; - } - - return $ini; - } - - private function parseEnvSection(string $content): array - { - $env = []; - - foreach (\explode("\n", \trim($content)) as $e) { - $e = \explode('=', \trim($e), 2); - - if (!empty($e[0]) && isset($e[1])) { - $env[$e[0]] = $e[1]; - } - } - - return $env; - } - - /** - * @throws Exception - */ - private function assertPhptExpectation(array $sections, string $output): void - { - $assertions = [ - 'EXPECT' => 'assertEquals', - 'EXPECTF' => 'assertStringMatchesFormat', - 'EXPECTREGEX' => 'assertRegExp', - ]; - - $actual = \preg_replace('/\r\n/', "\n", \trim($output)); - - foreach ($assertions as $sectionName => $sectionAssertion) { - if (isset($sections[$sectionName])) { - $sectionContent = \preg_replace('/\r\n/', "\n", \trim($sections[$sectionName])); - $expected = $sectionName === 'EXPECTREGEX' ? "/{$sectionContent}/" : $sectionContent; - - if ($expected === null) { - throw new Exception('No PHPT expectation found'); - } - Assert::$sectionAssertion($expected, $actual); - - return; - } - } - - throw new Exception('No PHPT assertion found'); - } - - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function runSkip(array &$sections, TestResult $result, array $settings): bool - { - if (!isset($sections['SKIPIF'])) { - return false; - } - - $skipif = $this->render($sections['SKIPIF']); - $jobResult = $this->phpUtil->runJob($skipif, $this->stringifyIni($settings)); - - if (!\strncasecmp('skip', \ltrim($jobResult['stdout']), 4)) { - $message = ''; - - if (\preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult['stdout'], $skipMatch)) { - $message = \substr($skipMatch[1], 2); - } - - $result->addFailure($this, new SkippedTestError($message), 0); - $result->endTest($this, 0); - - return true; - } - - return false; - } - - private function runClean(array &$sections): void - { - $this->phpUtil->setStdin(''); - $this->phpUtil->setArgs(''); - - if (isset($sections['CLEAN'])) { - $cleanCode = $this->render($sections['CLEAN']); - - $this->phpUtil->runJob($cleanCode, self::SETTINGS); - } - } - - /** - * @throws Exception - */ - private function parse(): array - { - $sections = []; - $section = ''; - - $unsupportedSections = [ - 'REDIRECTTEST', - 'REQUEST', - 'POST', - 'PUT', - 'POST_RAW', - 'GZIP_POST', - 'DEFLATE_POST', - 'GET', - 'COOKIE', - 'HEADERS', - 'CGI', - 'EXPECTHEADERS', - 'EXTENSIONS', - 'PHPDBG', - ]; - - foreach (\file($this->filename) as $line) { - if (\preg_match('/^--([_A-Z]+)--/', $line, $result)) { - $section = $result[1]; - $sections[$section] = ''; - - continue; - } - - if (empty($section)) { - throw new Exception('Invalid PHPT file: empty section header'); - } - - $sections[$section] .= $line; - } - - if (isset($sections['FILEEOF'])) { - $sections['FILE'] = \rtrim($sections['FILEEOF'], "\r\n"); - unset($sections['FILEEOF']); - } - - $this->parseExternal($sections); - - if (!$this->validate($sections)) { - throw new Exception('Invalid PHPT file'); - } - - foreach ($unsupportedSections as $section) { - if (isset($sections[$section])) { - throw new Exception( - "PHPUnit does not support PHPT $section sections" - ); - } - } - - return $sections; - } - - /** - * @throws Exception - */ - private function parseExternal(array &$sections): void - { - $allowSections = [ - 'FILE', - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ]; - $testDirectory = \dirname($this->filename) . \DIRECTORY_SEPARATOR; - - foreach ($allowSections as $section) { - if (isset($sections[$section . '_EXTERNAL'])) { - $externalFilename = \trim($sections[$section . '_EXTERNAL']); - - if (!\is_file($testDirectory . $externalFilename) || - !\is_readable($testDirectory . $externalFilename)) { - throw new Exception( - \sprintf( - 'Could not load --%s-- %s for PHPT file', - $section . '_EXTERNAL', - $testDirectory . $externalFilename - ) - ); - } - - $sections[$section] = \file_get_contents($testDirectory . $externalFilename); - - unset($sections[$section . '_EXTERNAL']); - } - } - } - - private function validate(array &$sections): bool - { - $requiredSections = [ - 'FILE', - [ - 'EXPECT', - 'EXPECTF', - 'EXPECTREGEX', - ], - ]; - - foreach ($requiredSections as $section) { - if (\is_array($section)) { - $foundSection = false; - - foreach ($section as $anySection) { - if (isset($sections[$anySection])) { - $foundSection = true; - - break; - } - } - - if (!$foundSection) { - return false; - } - - continue; - } - - if (!isset($sections[$section])) { - return false; - } - } - - return true; - } - - private function render(string $code): string - { - return \str_replace( - [ - '__DIR__', - '__FILE__', - ], - [ - "'" . \dirname($this->filename) . "'", - "'" . $this->filename . "'", - ], - $code - ); - } - - private function getCoverageFiles(): array - { - $baseDir = \dirname(\realpath($this->filename)) . \DIRECTORY_SEPARATOR; - $basename = \basename($this->filename, 'phpt'); - - return [ - 'coverage' => $baseDir . $basename . 'coverage', - 'job' => $baseDir . $basename . 'php', - ]; - } - - private function renderForCoverage(string &$job): void - { - $files = $this->getCoverageFiles(); - - $template = new Text_Template( - __DIR__ . '/../Util/PHP/Template/PhptTestCase.tpl' - ); - - $composerAutoload = '\'\''; - - if (\defined('PHPUNIT_COMPOSER_INSTALL') && !\defined('PHPUNIT_TESTSUITE')) { - $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true); - } - - $phar = '\'\''; - - if (\defined('__PHPUNIT_PHAR__')) { - $phar = \var_export(__PHPUNIT_PHAR__, true); - } - - $globals = ''; - - if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export( - $GLOBALS['__PHPUNIT_BOOTSTRAP'], - true - ) . ";\n"; - } - - $template->setVar( - [ - 'composerAutoload' => $composerAutoload, - 'phar' => $phar, - 'globals' => $globals, - 'job' => $files['job'], - 'coverageFile' => $files['coverage'], - ] - ); - - \file_put_contents($files['job'], $job); - $job = $template->render(); - } - - private function cleanupForCoverage(): array - { - $files = $this->getCoverageFiles(); - $coverage = @\unserialize(\file_get_contents($files['coverage'])); - - if ($coverage === false) { - $coverage = []; - } - - foreach ($files as $file) { - @\unlink($file); - } - - return $coverage; - } - - private function stringifyIni(array $ini): array - { - $settings = []; - - foreach ($ini as $key => $value) { - if (\is_array($value)) { - foreach ($value as $val) { - $settings[] = $key . '=' . $val; - } - - continue; - } - - $settings[] = $key . '=' . $value; - } - - return $settings; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -class IncludeGroupFilterIterator extends GroupFilterIterator -{ - protected function doAccept(string $hash): bool - { - return \in_array($hash, $this->groupTests, true); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\WarningTestCase; -use PHPUnit\Util\RegularExpression; -use RecursiveFilterIterator; -use RecursiveIterator; - -class NameFilterIterator extends RecursiveFilterIterator -{ - /** - * @var string - */ - protected $filter; - - /** - * @var int - */ - protected $filterMin; - - /** - * @var int - */ - protected $filterMax; - - /** - * @throws \Exception - */ - public function __construct(RecursiveIterator $iterator, string $filter) - { - parent::__construct($iterator); - - $this->setFilter($filter); - } - - public function accept(): bool - { - $test = $this->getInnerIterator()->current(); - - if ($test instanceof TestSuite) { - return true; - } - - $tmp = \PHPUnit\Util\Test::describe($test); - - if ($test instanceof WarningTestCase) { - $name = $test->getMessage(); - } else { - if ($tmp[0] !== '') { - $name = \implode('::', $tmp); - } else { - $name = $tmp[1]; - } - } - - $accepted = @\preg_match($this->filter, $name, $matches); - - if ($accepted && isset($this->filterMax)) { - $set = \end($matches); - $accepted = $set >= $this->filterMin && $set <= $this->filterMax; - } - - return (bool) $accepted; - } - - /** - * @throws \Exception - */ - protected function setFilter(string $filter): void - { - if (RegularExpression::safeMatch($filter, '') === false) { - // Handles: - // * testAssertEqualsSucceeds#4 - // * testAssertEqualsSucceeds#4-8 - if (\preg_match('/^(.*?)#(\d+)(?:-(\d+))?$/', $filter, $matches)) { - if (isset($matches[3]) && $matches[2] < $matches[3]) { - $filter = \sprintf( - '%s.*with data set #(\d+)$', - $matches[1] - ); - - $this->filterMin = $matches[2]; - $this->filterMax = $matches[3]; - } else { - $filter = \sprintf( - '%s.*with data set #%s$', - $matches[1], - $matches[2] - ); - } - } // Handles: - // * testDetermineJsonError@JSON_ERROR_NONE - // * testDetermineJsonError@JSON.* - elseif (\preg_match('/^(.*?)@(.+)$/', $filter, $matches)) { - $filter = \sprintf( - '%s.*with data set "%s"$', - $matches[1], - $matches[2] - ); - } - - // Escape delimiters in regular expression. Do NOT use preg_quote, - // to keep magic characters. - $filter = \sprintf('/%s/i', \str_replace( - '/', - '\\/', - $filter - )); - } - - $this->filter = $filter; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use FilterIterator; -use InvalidArgumentException; -use Iterator; -use PHPUnit\Framework\TestSuite; -use ReflectionClass; - -class Factory -{ - /** - * @var array - */ - private $filters = []; - - /** - * @throws InvalidArgumentException - */ - public function addFilter(ReflectionClass $filter, $args): void - { - if (!$filter->isSubclassOf(\RecursiveFilterIterator::class)) { - throw new InvalidArgumentException( - \sprintf( - 'Class "%s" does not extend RecursiveFilterIterator', - $filter->name - ) - ); - } - - $this->filters[] = [$filter, $args]; - } - - public function factory(Iterator $iterator, TestSuite $suite): FilterIterator - { - foreach ($this->filters as $filter) { - [$class, $args] = $filter; - $iterator = $class->newInstance($iterator, $args, $suite); - } - - return $iterator; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -use PHPUnit\Framework\TestSuite; -use RecursiveFilterIterator; -use RecursiveIterator; - -abstract class GroupFilterIterator extends RecursiveFilterIterator -{ - /** - * @var string[] - */ - protected $groupTests = []; - - public function __construct(RecursiveIterator $iterator, array $groups, TestSuite $suite) - { - parent::__construct($iterator); - - foreach ($suite->getGroupDetails() as $group => $tests) { - if (\in_array((string) $group, $groups, true)) { - $testHashes = \array_map( - 'spl_object_hash', - $tests - ); - - $this->groupTests = \array_merge($this->groupTests, $testHashes); - } - } - } - - public function accept(): bool - { - $test = $this->getInnerIterator()->current(); - - if ($test instanceof TestSuite) { - return true; - } - - return $this->doAccept(\spl_object_hash($test)); - } - - abstract protected function doAccept(string $hash); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner\Filter; - -class ExcludeGroupFilterIterator extends GroupFilterIterator -{ - protected function doAccept(string $hash): bool - { - return !\in_array($hash, $this->groupTests, true); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestSuite; -use ReflectionClass; -use ReflectionException; -use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; - -/** - * Base class for all test runners. - */ -abstract class BaseTestRunner -{ - public const STATUS_UNKNOWN = -1; - - public const STATUS_PASSED = 0; - - public const STATUS_SKIPPED = 1; - - public const STATUS_INCOMPLETE = 2; - - public const STATUS_FAILURE = 3; - - public const STATUS_ERROR = 4; - - public const STATUS_RISKY = 5; - - public const STATUS_WARNING = 6; - - public const SUITE_METHODNAME = 'suite'; - - /** - * Returns the loader to be used. - */ - public function getLoader(): TestSuiteLoader - { - return new StandardTestSuiteLoader; - } - - /** - * Returns the Test corresponding to the given suite. - * This is a template method, subclasses override - * the runFailed() and clearStatus() methods. - * - * @param array|string $suffixes - * - * @throws Exception - */ - public function getTest(string $suiteClassName, string $suiteClassFile = '', $suffixes = ''): ?Test - { - if (\is_dir($suiteClassName) && - !\is_file($suiteClassName . '.php') && empty($suiteClassFile)) { - $facade = new FileIteratorFacade; - $files = $facade->getFilesAsArray( - $suiteClassName, - $suffixes - ); - - $suite = new TestSuite($suiteClassName); - $suite->addTestFiles($files); - - return $suite; - } - - try { - $testClass = $this->loadSuiteClass( - $suiteClassName, - $suiteClassFile - ); - } catch (Exception $e) { - $this->runFailed($e->getMessage()); - - return null; - } - - try { - $suiteMethod = $testClass->getMethod(self::SUITE_METHODNAME); - - if (!$suiteMethod->isStatic()) { - $this->runFailed( - 'suite() method must be static.' - ); - - return null; - } - - try { - $test = $suiteMethod->invoke(null, $testClass->getName()); - } catch (ReflectionException $e) { - $this->runFailed( - \sprintf( - "Failed to invoke suite() method.\n%s", - $e->getMessage() - ) - ); - - return null; - } - } catch (ReflectionException $e) { - try { - $test = new TestSuite($testClass); - } catch (Exception $e) { - $test = new TestSuite; - $test->setName($suiteClassName); - } - } - - $this->clearStatus(); - - return $test; - } - - /** - * Returns the loaded ReflectionClass for a suite name. - */ - protected function loadSuiteClass(string $suiteClassName, string $suiteClassFile = ''): ReflectionClass - { - $loader = $this->getLoader(); - - return $loader->load($suiteClassName, $suiteClassFile); - } - - /** - * Clears the status message. - */ - protected function clearStatus(): void - { - } - - /** - * Override to define how to handle a failed loading of - * a test suite. - */ - abstract protected function runFailed(string $message); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit; - -/** - * Marker interface for PHPUnit exceptions. - */ -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI; - -use PharIo\Manifest\ApplicationName; -use PharIo\Manifest\Exception as ManifestException; -use PharIo\Manifest\ManifestLoader; -use PharIo\Version\Version as PharIoVersion; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Runner\StandardTestSuiteLoader; -use PHPUnit\Runner\TestSuiteLoader; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\Runner\Version; -use PHPUnit\Util\Configuration; -use PHPUnit\Util\ConfigurationGenerator; -use PHPUnit\Util\FileLoader; -use PHPUnit\Util\Filesystem; -use PHPUnit\Util\Getopt; -use PHPUnit\Util\Log\TeamCity; -use PHPUnit\Util\Printer; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\Util\TextTestListRenderer; -use PHPUnit\Util\XmlTestListRenderer; -use ReflectionClass; -use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; - -use Throwable; - -/** - * A TestRunner for the Command Line Interface (CLI) - * PHP SAPI Module. - */ -class Command -{ - /** - * @var array - */ - protected $arguments = [ - 'listGroups' => false, - 'listSuites' => false, - 'listTests' => false, - 'listTestsXml' => false, - 'loader' => null, - 'useDefaultConfiguration' => true, - 'loadedExtensions' => [], - 'notLoadedExtensions' => [], - ]; - - /** - * @var array - */ - protected $options = []; - - /** - * @var array - */ - protected $longOptions = [ - 'atleast-version=' => null, - 'prepend=' => null, - 'bootstrap=' => null, - 'cache-result' => null, - 'cache-result-file=' => null, - 'check-version' => null, - 'colors==' => null, - 'columns=' => null, - 'configuration=' => null, - 'coverage-clover=' => null, - 'coverage-crap4j=' => null, - 'coverage-html=' => null, - 'coverage-php=' => null, - 'coverage-text==' => null, - 'coverage-xml=' => null, - 'debug' => null, - 'disallow-test-output' => null, - 'disallow-resource-usage' => null, - 'disallow-todo-tests' => null, - 'default-time-limit=' => null, - 'enforce-time-limit' => null, - 'exclude-group=' => null, - 'filter=' => null, - 'generate-configuration' => null, - 'globals-backup' => null, - 'group=' => null, - 'help' => null, - 'resolve-dependencies' => null, - 'ignore-dependencies' => null, - 'include-path=' => null, - 'list-groups' => null, - 'list-suites' => null, - 'list-tests' => null, - 'list-tests-xml=' => null, - 'loader=' => null, - 'log-junit=' => null, - 'log-teamcity=' => null, - 'no-configuration' => null, - 'no-coverage' => null, - 'no-logging' => null, - 'no-extensions' => null, - 'order-by=' => null, - 'printer=' => null, - 'process-isolation' => null, - 'repeat=' => null, - 'dont-report-useless-tests' => null, - 'random-order' => null, - 'random-order-seed=' => null, - 'reverse-order' => null, - 'reverse-list' => null, - 'static-backup' => null, - 'stderr' => null, - 'stop-on-defect' => null, - 'stop-on-error' => null, - 'stop-on-failure' => null, - 'stop-on-warning' => null, - 'stop-on-incomplete' => null, - 'stop-on-risky' => null, - 'stop-on-skipped' => null, - 'fail-on-warning' => null, - 'fail-on-risky' => null, - 'strict-coverage' => null, - 'disable-coverage-ignore' => null, - 'strict-global-state' => null, - 'teamcity' => null, - 'testdox' => null, - 'testdox-group=' => null, - 'testdox-exclude-group=' => null, - 'testdox-html=' => null, - 'testdox-text=' => null, - 'testdox-xml=' => null, - 'test-suffix=' => null, - 'testsuite=' => null, - 'verbose' => null, - 'version' => null, - 'whitelist=' => null, - 'dump-xdebug-filter=' => null, - ]; - - /** - * @var bool - */ - private $versionStringPrinted = false; - - /** - * @throws \RuntimeException - * @throws \PHPUnit\Framework\Exception - * @throws \InvalidArgumentException - */ - public static function main(bool $exit = true): int - { - $command = new static; - - return $command->run($_SERVER['argv'], $exit); - } - - /** - * @throws \RuntimeException - * @throws \ReflectionException - * @throws \InvalidArgumentException - * @throws Exception - */ - public function run(array $argv, bool $exit = true): int - { - $this->handleArguments($argv); - - $runner = $this->createRunner(); - - if ($this->arguments['test'] instanceof Test) { - $suite = $this->arguments['test']; - } else { - $suite = $runner->getTest( - $this->arguments['test'], - $this->arguments['testFile'], - $this->arguments['testSuffixes'] - ); - } - - if ($this->arguments['listGroups']) { - return $this->handleListGroups($suite, $exit); - } - - if ($this->arguments['listSuites']) { - return $this->handleListSuites($exit); - } - - if ($this->arguments['listTests']) { - return $this->handleListTests($suite, $exit); - } - - if ($this->arguments['listTestsXml']) { - return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit); - } - - unset($this->arguments['test'], $this->arguments['testFile']); - - try { - $result = $runner->doRun($suite, $this->arguments, $exit); - } catch (Exception $e) { - print $e->getMessage() . \PHP_EOL; - } - - $return = TestRunner::FAILURE_EXIT; - - if (isset($result) && $result->wasSuccessful()) { - $return = TestRunner::SUCCESS_EXIT; - } elseif (!isset($result) || $result->errorCount() > 0) { - $return = TestRunner::EXCEPTION_EXIT; - } - - if ($exit) { - exit($return); - } - - return $return; - } - - /** - * Create a TestRunner, override in subclasses. - */ - protected function createRunner(): TestRunner - { - return new TestRunner($this->arguments['loader']); - } - - /** - * Handles the command-line arguments. - * - * A child class of PHPUnit\TextUI\Command can hook into the argument - * parsing by adding the switch(es) to the $longOptions array and point to a - * callback method that handles the switch(es) in the child class like this - * - * - * longOptions['my-switch'] = 'myHandler'; - * // my-secondswitch will accept a value - note the equals sign - * $this->longOptions['my-secondswitch='] = 'myOtherHandler'; - * } - * - * // --my-switch -> myHandler() - * protected function myHandler() - * { - * } - * - * // --my-secondswitch foo -> myOtherHandler('foo') - * protected function myOtherHandler ($value) - * { - * } - * - * // You will also need this - the static keyword in the - * // PHPUnit\TextUI\Command will mean that it'll be - * // PHPUnit\TextUI\Command that gets instantiated, - * // not MyCommand - * public static function main($exit = true) - * { - * $command = new static; - * - * return $command->run($_SERVER['argv'], $exit); - * } - * - * } - * - * - * @throws Exception - */ - protected function handleArguments(array $argv): void - { - try { - $this->options = Getopt::getopt( - $argv, - 'd:c:hv', - \array_keys($this->longOptions) - ); - } catch (Exception $t) { - $this->exitWithErrorMessage($t->getMessage()); - } - - foreach ($this->options[0] as $option) { - switch ($option[0]) { - case '--colors': - $this->arguments['colors'] = $option[1] ?: ResultPrinter::COLOR_AUTO; - - break; - - case '--bootstrap': - $this->arguments['bootstrap'] = $option[1]; - - break; - - case '--cache-result': - $this->arguments['cacheResult'] = true; - - break; - - case '--cache-result-file': - $this->arguments['cacheResultFile'] = $option[1]; - - break; - - case '--columns': - if (\is_numeric($option[1])) { - $this->arguments['columns'] = (int) $option[1]; - } elseif ($option[1] === 'max') { - $this->arguments['columns'] = 'max'; - } - - break; - - case 'c': - case '--configuration': - $this->arguments['configuration'] = $option[1]; - - break; - - case '--coverage-clover': - $this->arguments['coverageClover'] = $option[1]; - - break; - - case '--coverage-crap4j': - $this->arguments['coverageCrap4J'] = $option[1]; - - break; - - case '--coverage-html': - $this->arguments['coverageHtml'] = $option[1]; - - break; - - case '--coverage-php': - $this->arguments['coveragePHP'] = $option[1]; - - break; - - case '--coverage-text': - if ($option[1] === null) { - $option[1] = 'php://stdout'; - } - - $this->arguments['coverageText'] = $option[1]; - $this->arguments['coverageTextShowUncoveredFiles'] = false; - $this->arguments['coverageTextShowOnlySummary'] = false; - - break; - - case '--coverage-xml': - $this->arguments['coverageXml'] = $option[1]; - - break; - - case 'd': - $ini = \explode('=', $option[1]); - - if (isset($ini[0])) { - if (isset($ini[1])) { - \ini_set($ini[0], $ini[1]); - } else { - \ini_set($ini[0], true); - } - } - - break; - - case '--debug': - $this->arguments['debug'] = true; - - break; - - case 'h': - case '--help': - $this->showHelp(); - exit(TestRunner::SUCCESS_EXIT); - - break; - - case '--filter': - $this->arguments['filter'] = $option[1]; - - break; - - case '--testsuite': - $this->arguments['testsuite'] = $option[1]; - - break; - - case '--generate-configuration': - $this->printVersionString(); - - print 'Generating phpunit.xml in ' . \getcwd() . \PHP_EOL . \PHP_EOL; - - print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): '; - $bootstrapScript = \trim(\fgets(\STDIN)); - - print 'Tests directory (relative to path shown above; default: tests): '; - $testsDirectory = \trim(\fgets(\STDIN)); - - print 'Source directory (relative to path shown above; default: src): '; - $src = \trim(\fgets(\STDIN)); - - if ($bootstrapScript === '') { - $bootstrapScript = 'vendor/autoload.php'; - } - - if ($testsDirectory === '') { - $testsDirectory = 'tests'; - } - - if ($src === '') { - $src = 'src'; - } - - $generator = new ConfigurationGenerator; - - \file_put_contents( - 'phpunit.xml', - $generator->generateDefaultConfiguration( - Version::series(), - $bootstrapScript, - $testsDirectory, - $src - ) - ); - - print \PHP_EOL . 'Generated phpunit.xml in ' . \getcwd() . \PHP_EOL; - - exit(TestRunner::SUCCESS_EXIT); - - break; - - case '--group': - $this->arguments['groups'] = \explode(',', $option[1]); - - break; - - case '--exclude-group': - $this->arguments['excludeGroups'] = \explode( - ',', - $option[1] - ); - - break; - - case '--test-suffix': - $this->arguments['testSuffixes'] = \explode( - ',', - $option[1] - ); - - break; - - case '--include-path': - $includePath = $option[1]; - - break; - - case '--list-groups': - $this->arguments['listGroups'] = true; - - break; - - case '--list-suites': - $this->arguments['listSuites'] = true; - - break; - - case '--list-tests': - $this->arguments['listTests'] = true; - - break; - - case '--list-tests-xml': - $this->arguments['listTestsXml'] = $option[1]; - - break; - - case '--printer': - $this->arguments['printer'] = $option[1]; - - break; - - case '--loader': - $this->arguments['loader'] = $option[1]; - - break; - - case '--log-junit': - $this->arguments['junitLogfile'] = $option[1]; - - break; - - case '--log-teamcity': - $this->arguments['teamcityLogfile'] = $option[1]; - - break; - - case '--order-by': - $this->handleOrderByOption($option[1]); - - break; - - case '--process-isolation': - $this->arguments['processIsolation'] = true; - - break; - - case '--repeat': - $this->arguments['repeat'] = (int) $option[1]; - - break; - - case '--stderr': - $this->arguments['stderr'] = true; - - break; - - case '--stop-on-defect': - $this->arguments['stopOnDefect'] = true; - - break; - - case '--stop-on-error': - $this->arguments['stopOnError'] = true; - - break; - - case '--stop-on-failure': - $this->arguments['stopOnFailure'] = true; - - break; - - case '--stop-on-warning': - $this->arguments['stopOnWarning'] = true; - - break; - - case '--stop-on-incomplete': - $this->arguments['stopOnIncomplete'] = true; - - break; - - case '--stop-on-risky': - $this->arguments['stopOnRisky'] = true; - - break; - - case '--stop-on-skipped': - $this->arguments['stopOnSkipped'] = true; - - break; - - case '--fail-on-warning': - $this->arguments['failOnWarning'] = true; - - break; - - case '--fail-on-risky': - $this->arguments['failOnRisky'] = true; - - break; - - case '--teamcity': - $this->arguments['printer'] = TeamCity::class; - - break; - - case '--testdox': - $this->arguments['printer'] = CliTestDoxPrinter::class; - - break; - - case '--testdox-group': - $this->arguments['testdoxGroups'] = \explode( - ',', - $option[1] - ); - - break; - - case '--testdox-exclude-group': - $this->arguments['testdoxExcludeGroups'] = \explode( - ',', - $option[1] - ); - - break; - - case '--testdox-html': - $this->arguments['testdoxHTMLFile'] = $option[1]; - - break; - - case '--testdox-text': - $this->arguments['testdoxTextFile'] = $option[1]; - - break; - - case '--testdox-xml': - $this->arguments['testdoxXMLFile'] = $option[1]; - - break; - - case '--no-configuration': - $this->arguments['useDefaultConfiguration'] = false; - - break; - - case '--no-extensions': - $this->arguments['noExtensions'] = true; - - break; - - case '--no-coverage': - $this->arguments['noCoverage'] = true; - - break; - - case '--no-logging': - $this->arguments['noLogging'] = true; - - break; - - case '--globals-backup': - $this->arguments['backupGlobals'] = true; - - break; - - case '--static-backup': - $this->arguments['backupStaticAttributes'] = true; - - break; - - case 'v': - case '--verbose': - $this->arguments['verbose'] = true; - - break; - - case '--atleast-version': - if (\version_compare(Version::id(), $option[1], '>=')) { - exit(TestRunner::SUCCESS_EXIT); - } - - exit(TestRunner::FAILURE_EXIT); - - break; - - case '--version': - $this->printVersionString(); - exit(TestRunner::SUCCESS_EXIT); - - break; - - case '--dont-report-useless-tests': - $this->arguments['reportUselessTests'] = false; - - break; - - case '--strict-coverage': - $this->arguments['strictCoverage'] = true; - - break; - - case '--disable-coverage-ignore': - $this->arguments['disableCodeCoverageIgnore'] = true; - - break; - - case '--strict-global-state': - $this->arguments['beStrictAboutChangesToGlobalState'] = true; - - break; - - case '--disallow-test-output': - $this->arguments['disallowTestOutput'] = true; - - break; - - case '--disallow-resource-usage': - $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true; - - break; - - case '--default-time-limit': - $this->arguments['defaultTimeLimit'] = (int) $option[1]; - - break; - - case '--enforce-time-limit': - $this->arguments['enforceTimeLimit'] = true; - - break; - - case '--disallow-todo-tests': - $this->arguments['disallowTodoAnnotatedTests'] = true; - - break; - - case '--reverse-list': - $this->arguments['reverseList'] = true; - - break; - - case '--check-version': - $this->handleVersionCheck(); - - break; - - case '--whitelist': - $this->arguments['whitelist'] = $option[1]; - - break; - - case '--random-order': - $this->handleOrderByOption('random'); - - break; - - case '--random-order-seed': - $this->arguments['randomOrderSeed'] = (int) $option[1]; - - break; - - case '--resolve-dependencies': - $this->handleOrderByOption('depends'); - - break; - - case '--ignore-dependencies': - $this->arguments['resolveDependencies'] = false; - - break; - - case '--reverse-order': - $this->handleOrderByOption('reverse'); - - break; - - case '--dump-xdebug-filter': - $this->arguments['xdebugFilterFile'] = $option[1]; - - break; - - default: - $optionName = \str_replace('--', '', $option[0]); - - $handler = null; - - if (isset($this->longOptions[$optionName])) { - $handler = $this->longOptions[$optionName]; - } elseif (isset($this->longOptions[$optionName . '='])) { - $handler = $this->longOptions[$optionName . '=']; - } - - if (isset($handler) && \is_callable([$this, $handler])) { - $this->$handler($option[1]); - } - } - } - - $this->handleCustomTestSuite(); - - if (!isset($this->arguments['test'])) { - if (isset($this->options[1][0])) { - $this->arguments['test'] = $this->options[1][0]; - } - - if (isset($this->options[1][1])) { - $this->arguments['testFile'] = \realpath($this->options[1][1]); - } else { - $this->arguments['testFile'] = ''; - } - - if (isset($this->arguments['test']) && - \is_file($this->arguments['test']) && - \substr($this->arguments['test'], -5, 5) != '.phpt') { - $this->arguments['testFile'] = \realpath($this->arguments['test']); - $this->arguments['test'] = \substr($this->arguments['test'], 0, \strrpos($this->arguments['test'], '.')); - } - } - - if (!isset($this->arguments['testSuffixes'])) { - $this->arguments['testSuffixes'] = ['Test.php', '.phpt']; - } - - if (isset($includePath)) { - \ini_set( - 'include_path', - $includePath . \PATH_SEPARATOR . \ini_get('include_path') - ); - } - - if ($this->arguments['loader'] !== null) { - $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']); - } - - if (isset($this->arguments['configuration']) && - \is_dir($this->arguments['configuration'])) { - $configurationFile = $this->arguments['configuration'] . '/phpunit.xml'; - - if (\file_exists($configurationFile)) { - $this->arguments['configuration'] = \realpath( - $configurationFile - ); - } elseif (\file_exists($configurationFile . '.dist')) { - $this->arguments['configuration'] = \realpath( - $configurationFile . '.dist' - ); - } - } elseif (!isset($this->arguments['configuration']) && - $this->arguments['useDefaultConfiguration']) { - if (\file_exists('phpunit.xml')) { - $this->arguments['configuration'] = \realpath('phpunit.xml'); - } elseif (\file_exists('phpunit.xml.dist')) { - $this->arguments['configuration'] = \realpath( - 'phpunit.xml.dist' - ); - } - } - - if (isset($this->arguments['configuration'])) { - try { - $configuration = Configuration::getInstance( - $this->arguments['configuration'] - ); - } catch (Throwable $t) { - print $t->getMessage() . \PHP_EOL; - exit(TestRunner::FAILURE_EXIT); - } - - $phpunitConfiguration = $configuration->getPHPUnitConfiguration(); - - $configuration->handlePHPConfiguration(); - - /* - * Issue #1216 - */ - if (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap']); - } elseif (isset($phpunitConfiguration['bootstrap'])) { - $this->handleBootstrap($phpunitConfiguration['bootstrap']); - } - - /* - * Issue #657 - */ - if (isset($phpunitConfiguration['stderr']) && !isset($this->arguments['stderr'])) { - $this->arguments['stderr'] = $phpunitConfiguration['stderr']; - } - - if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && \extension_loaded('phar')) { - $this->handleExtensions($phpunitConfiguration['extensionsDirectory']); - } - - if (isset($phpunitConfiguration['columns']) && !isset($this->arguments['columns'])) { - $this->arguments['columns'] = $phpunitConfiguration['columns']; - } - - if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) { - if (isset($phpunitConfiguration['printerFile'])) { - $file = $phpunitConfiguration['printerFile']; - } else { - $file = ''; - } - - $this->arguments['printer'] = $this->handlePrinter( - $phpunitConfiguration['printerClass'], - $file - ); - } - - if (isset($phpunitConfiguration['testSuiteLoaderClass'])) { - if (isset($phpunitConfiguration['testSuiteLoaderFile'])) { - $file = $phpunitConfiguration['testSuiteLoaderFile']; - } else { - $file = ''; - } - - $this->arguments['loader'] = $this->handleLoader( - $phpunitConfiguration['testSuiteLoaderClass'], - $file - ); - } - - if (!isset($this->arguments['testsuite']) && isset($phpunitConfiguration['defaultTestSuite'])) { - $this->arguments['testsuite'] = $phpunitConfiguration['defaultTestSuite']; - } - - if (!isset($this->arguments['test'])) { - $testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? ''); - - if ($testSuite !== null) { - $this->arguments['test'] = $testSuite; - } - } - } elseif (isset($this->arguments['bootstrap'])) { - $this->handleBootstrap($this->arguments['bootstrap']); - } - - if (isset($this->arguments['printer']) && - \is_string($this->arguments['printer'])) { - $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']); - } - - if (isset($this->arguments['test']) && \is_string($this->arguments['test']) && \substr($this->arguments['test'], -5, 5) == '.phpt') { - $test = new PhptTestCase($this->arguments['test']); - - $this->arguments['test'] = new TestSuite; - $this->arguments['test']->addTest($test); - } - - if (!isset($this->arguments['test'])) { - $this->showHelp(); - exit(TestRunner::EXCEPTION_EXIT); - } - } - - /** - * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation. - */ - protected function handleLoader(string $loaderClass, string $loaderFile = ''): ?TestSuiteLoader - { - if (!\class_exists($loaderClass, false)) { - if ($loaderFile == '') { - $loaderFile = Filesystem::classNameToFilename( - $loaderClass - ); - } - - $loaderFile = \stream_resolve_include_path($loaderFile); - - if ($loaderFile) { - require $loaderFile; - } - } - - if (\class_exists($loaderClass, false)) { - $class = new ReflectionClass($loaderClass); - - if ($class->implementsInterface(TestSuiteLoader::class) && - $class->isInstantiable()) { - return $class->newInstance(); - } - } - - if ($loaderClass == StandardTestSuiteLoader::class) { - return null; - } - - $this->exitWithErrorMessage( - \sprintf( - 'Could not use "%s" as loader.', - $loaderClass - ) - ); - - return null; - } - - /** - * Handles the loading of the PHPUnit\Util\Printer implementation. - * - * @return null|Printer|string - */ - protected function handlePrinter(string $printerClass, string $printerFile = '') - { - if (!\class_exists($printerClass, false)) { - if ($printerFile == '') { - $printerFile = Filesystem::classNameToFilename( - $printerClass - ); - } - - $printerFile = \stream_resolve_include_path($printerFile); - - if ($printerFile) { - require $printerFile; - } - } - - if (!\class_exists($printerClass)) { - $this->exitWithErrorMessage( - \sprintf( - 'Could not use "%s" as printer: class does not exist', - $printerClass - ) - ); - } - - $class = new ReflectionClass($printerClass); - - if (!$class->implementsInterface(TestListener::class)) { - $this->exitWithErrorMessage( - \sprintf( - 'Could not use "%s" as printer: class does not implement %s', - $printerClass, - TestListener::class - ) - ); - } - - if (!$class->isSubclassOf(Printer::class)) { - $this->exitWithErrorMessage( - \sprintf( - 'Could not use "%s" as printer: class does not extend %s', - $printerClass, - Printer::class - ) - ); - } - - if (!$class->isInstantiable()) { - $this->exitWithErrorMessage( - \sprintf( - 'Could not use "%s" as printer: class cannot be instantiated', - $printerClass - ) - ); - } - - if ($class->isSubclassOf(ResultPrinter::class)) { - return $printerClass; - } - - $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null; - - return $class->newInstance($outputStream); - } - - /** - * Loads a bootstrap file. - */ - protected function handleBootstrap(string $filename): void - { - try { - FileLoader::checkAndLoad($filename); - } catch (Exception $e) { - $this->exitWithErrorMessage($e->getMessage()); - } - } - - protected function handleVersionCheck(): void - { - $this->printVersionString(); - - $latestVersion = \file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit'); - $isOutdated = \version_compare($latestVersion, Version::id(), '>'); - - if ($isOutdated) { - \printf( - 'You are not using the latest version of PHPUnit.' . \PHP_EOL . - 'The latest version is PHPUnit %s.' . \PHP_EOL, - $latestVersion - ); - } else { - print 'You are using the latest version of PHPUnit.' . \PHP_EOL; - } - - exit(TestRunner::SUCCESS_EXIT); - } - - /** - * Show the help message. - */ - protected function showHelp(): void - { - $this->printVersionString(); - - print << - -Code Coverage Options: - - --coverage-clover Generate code coverage report in Clover XML format - --coverage-crap4j Generate code coverage report in Crap4J XML format - --coverage-html Generate code coverage report in HTML format - --coverage-php Export PHP_CodeCoverage object to file - --coverage-text= Generate code coverage report in text format - Default: Standard output - --coverage-xml Generate code coverage report in PHPUnit XML format - --whitelist Whitelist for code coverage analysis - --disable-coverage-ignore Disable annotations for ignoring code coverage - --no-coverage Ignore code coverage configuration - --dump-xdebug-filter Generate script to set Xdebug code coverage filter - -Logging Options: - - --log-junit Log test execution in JUnit XML format to file - --log-teamcity Log test execution in TeamCity format to file - --testdox-html Write agile documentation in HTML format to file - --testdox-text Write agile documentation in Text format to file - --testdox-xml Write agile documentation in XML format to file - --reverse-list Print defects in reverse order - -Test Selection Options: - - --filter Filter which tests to run - --testsuite Filter which testsuite to run - --group ... Only runs tests from the specified group(s) - --exclude-group ... Exclude tests from the specified group(s) - --list-groups List available test groups - --list-suites List available test suites - --list-tests List available tests - --list-tests-xml List available tests in XML format - --test-suffix ... Only search for test in files with specified - suffix(es). Default: Test.php,.phpt - -Test Execution Options: - - --dont-report-useless-tests Do not report tests that do not test anything - --strict-coverage Be strict about @covers annotation usage - --strict-global-state Be strict about changes to global state - --disallow-test-output Be strict about output during tests - --disallow-resource-usage Be strict about resource usage during small tests - --enforce-time-limit Enforce time limit based on test size - --default-time-limit= Timeout in seconds for tests without @small, @medium or @large - --disallow-todo-tests Disallow @todo-annotated tests - - --process-isolation Run each test in a separate PHP process - --globals-backup Backup and restore \$GLOBALS for each test - --static-backup Backup and restore static attributes for each test - - --colors= Use colors in output ("never", "auto" or "always") - --columns Number of columns to use for progress output - --columns max Use maximum number of columns for progress output - --stderr Write to STDERR instead of STDOUT - --stop-on-defect Stop execution upon first not-passed test - --stop-on-error Stop execution upon first error - --stop-on-failure Stop execution upon first error or failure - --stop-on-warning Stop execution upon first warning - --stop-on-risky Stop execution upon first risky test - --stop-on-skipped Stop execution upon first skipped test - --stop-on-incomplete Stop execution upon first incomplete test - --fail-on-warning Treat tests with warnings as failures - --fail-on-risky Treat risky tests as failures - -v|--verbose Output more verbose information - --debug Display debugging information - - --loader TestSuiteLoader implementation to use - --repeat Runs the test(s) repeatedly - --teamcity Report test execution progress in TeamCity format - --testdox Report test execution progress in TestDox format - --testdox-group Only include tests from the specified group(s) - --testdox-exclude-group Exclude tests from the specified group(s) - --printer TestListener implementation to use - - --resolve-dependencies Resolve dependencies between tests - --order-by= Run tests in order: default|reverse|random|defects|depends - --random-order-seed= Use a specific random seed for random order - --cache-result Write run result to cache to enable ordering tests defects-first - -Configuration Options: - - --prepend A PHP script that is included as early as possible - --bootstrap A PHP script that is included before the tests run - -c|--configuration Read configuration from XML file - --no-configuration Ignore default configuration file (phpunit.xml) - --no-logging Ignore logging configuration - --no-extensions Do not load PHPUnit extensions - --include-path Prepend PHP's include_path with given path(s) - -d key[=value] Sets a php.ini value - --generate-configuration Generate configuration file with suggested settings - --cache-result-file= Specify result cache path and filename - -Miscellaneous Options: - - -h|--help Prints this usage information - --version Prints the version and exits - --atleast-version Checks that version is greater than min and exits - --check-version Check whether PHPUnit is the latest version - -EOT; - } - - /** - * Custom callback for test suite discovery. - */ - protected function handleCustomTestSuite(): void - { - } - - private function printVersionString(): void - { - if ($this->versionStringPrinted) { - return; - } - - print Version::getVersionString() . \PHP_EOL . \PHP_EOL; - - $this->versionStringPrinted = true; - } - - private function exitWithErrorMessage(string $message): void - { - $this->printVersionString(); - - print $message . \PHP_EOL; - - exit(TestRunner::FAILURE_EXIT); - } - - private function handleExtensions(string $directory): void - { - $facade = new FileIteratorFacade; - - foreach ($facade->getFilesAsArray($directory, '.phar') as $file) { - if (!\file_exists('phar://' . $file . '/manifest.xml')) { - $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit'; - - continue; - } - - try { - $applicationName = new ApplicationName('phpunit/phpunit'); - $version = new PharIoVersion(Version::series()); - $manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml'); - - if (!$manifest->isExtensionFor($applicationName)) { - $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit'; - - continue; - } - - if (!$manifest->isExtensionFor($applicationName, $version)) { - $this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit'; - - continue; - } - } catch (ManifestException $e) { - $this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage(); - - continue; - } - - require $file; - - $this->arguments['loadedExtensions'][] = $manifest->getName() . ' ' . $manifest->getVersion()->getVersionString(); - } - } - - private function handleListGroups(TestSuite $suite, bool $exit): int - { - $this->printVersionString(); - - print 'Available test group(s):' . \PHP_EOL; - - $groups = $suite->getGroups(); - \sort($groups); - - foreach ($groups as $group) { - \printf( - ' - %s' . \PHP_EOL, - $group - ); - } - - if ($exit) { - exit(TestRunner::SUCCESS_EXIT); - } - - return TestRunner::SUCCESS_EXIT; - } - - private function handleListSuites(bool $exit): int - { - $this->printVersionString(); - - print 'Available test suite(s):' . \PHP_EOL; - - $configuration = Configuration::getInstance( - $this->arguments['configuration'] - ); - - $suiteNames = $configuration->getTestSuiteNames(); - - foreach ($suiteNames as $suiteName) { - \printf( - ' - %s' . \PHP_EOL, - $suiteName - ); - } - - if ($exit) { - exit(TestRunner::SUCCESS_EXIT); - } - - return TestRunner::SUCCESS_EXIT; - } - - private function handleListTests(TestSuite $suite, bool $exit): int - { - $this->printVersionString(); - - $renderer = new TextTestListRenderer; - - print $renderer->render($suite); - - if ($exit) { - exit(TestRunner::SUCCESS_EXIT); - } - - return TestRunner::SUCCESS_EXIT; - } - - private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int - { - $this->printVersionString(); - - $renderer = new XmlTestListRenderer; - - \file_put_contents($target, $renderer->render($suite)); - - \printf( - 'Wrote list of tests that would have been run to %s' . \PHP_EOL, - $target - ); - - if ($exit) { - exit(TestRunner::SUCCESS_EXIT); - } - - return TestRunner::SUCCESS_EXIT; - } - - private function handleOrderByOption(string $value): void - { - foreach (\explode(',', $value) as $order) { - switch ($order) { - case 'default': - $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT; - $this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT; - $this->arguments['resolveDependencies'] = false; - - break; - - case 'reverse': - $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_REVERSED; - - break; - - case 'random': - $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED; - - break; - - case 'defects': - $this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST; - - break; - - case 'depends': - $this->arguments['resolveDependencies'] = true; - - break; - - default: - $this->exitWithErrorMessage("unrecognized --order-by option: $order"); - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI; - -use PHPUnit\Framework\Error\Deprecated; -use PHPUnit\Framework\Error\Notice; -use PHPUnit\Framework\Error\Warning; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestResult; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\AfterLastTestHook; -use PHPUnit\Runner\BaseTestRunner; -use PHPUnit\Runner\BeforeFirstTestHook; -use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator; -use PHPUnit\Runner\Filter\Factory; -use PHPUnit\Runner\Filter\IncludeGroupFilterIterator; -use PHPUnit\Runner\Filter\NameFilterIterator; -use PHPUnit\Runner\Hook; -use PHPUnit\Runner\NullTestResultCache; -use PHPUnit\Runner\ResultCacheExtension; -use PHPUnit\Runner\StandardTestSuiteLoader; -use PHPUnit\Runner\TestHook; -use PHPUnit\Runner\TestListenerAdapter; -use PHPUnit\Runner\TestResultCache; -use PHPUnit\Runner\TestSuiteLoader; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\Runner\Version; -use PHPUnit\Util\Configuration; -use PHPUnit\Util\Filesystem; -use PHPUnit\Util\Log\JUnit; -use PHPUnit\Util\Log\TeamCity; -use PHPUnit\Util\Printer; -use PHPUnit\Util\TestDox\CliTestDoxPrinter; -use PHPUnit\Util\TestDox\HtmlResultPrinter; -use PHPUnit\Util\TestDox\TextResultPrinter; -use PHPUnit\Util\TestDox\XmlResultPrinter; -use PHPUnit\Util\XdebugFilterScriptGenerator; -use ReflectionClass; -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException; -use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter; -use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport; -use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport; -use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport; -use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport; -use SebastianBergmann\CodeCoverage\Report\Text as TextReport; -use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport; -use SebastianBergmann\Comparator\Comparator; -use SebastianBergmann\Environment\Runtime; -use SebastianBergmann\Invoker\Invoker; - -/** - * A TestRunner for the Command Line Interface (CLI) - * PHP SAPI Module. - */ -class TestRunner extends BaseTestRunner -{ - public const SUCCESS_EXIT = 0; - - public const FAILURE_EXIT = 1; - - public const EXCEPTION_EXIT = 2; - - /** - * @var bool - */ - protected static $versionStringPrinted = false; - - /** - * @var CodeCoverageFilter - */ - protected $codeCoverageFilter; - - /** - * @var TestSuiteLoader - */ - protected $loader; - - /** - * @var ResultPrinter - */ - protected $printer; - - /** - * @var Runtime - */ - private $runtime; - - /** - * @var bool - */ - private $messagePrinted = false; - - /** - * @var Hook[] - */ - private $extensions = []; - - /** - * @param ReflectionClass|Test $test - * @param bool $exit - * - * @throws \RuntimeException - * @throws \InvalidArgumentException - * @throws Exception - * @throws \ReflectionException - */ - public static function run($test, array $arguments = [], $exit = true): TestResult - { - if ($test instanceof ReflectionClass) { - $test = new TestSuite($test); - } - - if ($test instanceof Test) { - $aTestRunner = new self; - - return $aTestRunner->doRun( - $test, - $arguments, - $exit - ); - } - - throw new Exception('No test case or test suite found.'); - } - - public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null) - { - if ($filter === null) { - $filter = new CodeCoverageFilter; - } - - $this->codeCoverageFilter = $filter; - $this->loader = $loader; - $this->runtime = new Runtime; - } - - /** - * @throws \PHPUnit\Runner\Exception - * @throws Exception - * @throws \InvalidArgumentException - * @throws \RuntimeException - * @throws \ReflectionException - */ - public function doRun(Test $suite, array $arguments = [], bool $exit = true): TestResult - { - if (isset($arguments['configuration'])) { - $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration']; - } - - $this->handleConfiguration($arguments); - - if (\is_int($arguments['columns']) && $arguments['columns'] < 16) { - $arguments['columns'] = 16; - $tooFewColumnsRequested = true; - } - - if (isset($arguments['bootstrap'])) { - $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap']; - } - - if ($suite instanceof TestCase || $suite instanceof TestSuite) { - if ($arguments['backupGlobals'] === true) { - $suite->setBackupGlobals(true); - } - - if ($arguments['backupStaticAttributes'] === true) { - $suite->setBackupStaticAttributes(true); - } - - if ($arguments['beStrictAboutChangesToGlobalState'] === true) { - $suite->setBeStrictAboutChangesToGlobalState(true); - } - } - - if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { - \mt_srand($arguments['randomOrderSeed']); - } - - if ($arguments['cacheResult']) { - if (isset($arguments['cacheResultFile'])) { - $cache = new TestResultCache($arguments['cacheResultFile']); - } else { - $cache = new TestResultCache; - } - - $this->extensions[] = new ResultCacheExtension($cache); - } - - if ($arguments['executionOrder'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) { - $cache = $cache ?? new NullTestResultCache; - - $cache->load(); - - $sorter = new TestSuiteSorter($cache); - - $sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']); - $originalExecutionOrder = $sorter->getOriginalExecutionOrder(); - - unset($sorter); - } - - if (\is_int($arguments['repeat']) && $arguments['repeat'] > 0) { - $_suite = new TestSuite; - - foreach (\range(1, $arguments['repeat']) as $step) { - $_suite->addTest($suite); - } - - $suite = $_suite; - - unset($_suite); - } - - $result = $this->createTestResult(); - - $listener = new TestListenerAdapter; - $listenerNeeded = false; - - foreach ($this->extensions as $extension) { - if ($extension instanceof TestHook) { - $listener->add($extension); - - $listenerNeeded = true; - } - } - - if ($listenerNeeded) { - $result->addListener($listener); - } - - unset($listener, $listenerNeeded); - - if (!$arguments['convertErrorsToExceptions']) { - $result->convertErrorsToExceptions(false); - } - - if (!$arguments['convertDeprecationsToExceptions']) { - Deprecated::$enabled = false; - } - - if (!$arguments['convertNoticesToExceptions']) { - Notice::$enabled = false; - } - - if (!$arguments['convertWarningsToExceptions']) { - Warning::$enabled = false; - } - - if ($arguments['stopOnError']) { - $result->stopOnError(true); - } - - if ($arguments['stopOnFailure']) { - $result->stopOnFailure(true); - } - - if ($arguments['stopOnWarning']) { - $result->stopOnWarning(true); - } - - if ($arguments['stopOnIncomplete']) { - $result->stopOnIncomplete(true); - } - - if ($arguments['stopOnRisky']) { - $result->stopOnRisky(true); - } - - if ($arguments['stopOnSkipped']) { - $result->stopOnSkipped(true); - } - - if ($arguments['stopOnDefect']) { - $result->stopOnDefect(true); - } - - if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) { - $result->setRegisterMockObjectsFromTestArgumentsRecursively(true); - } - - if ($this->printer === null) { - if (isset($arguments['printer']) && - $arguments['printer'] instanceof Printer) { - $this->printer = $arguments['printer']; - } else { - $printerClass = ResultPrinter::class; - - if (isset($arguments['printer']) && \is_string($arguments['printer']) && \class_exists($arguments['printer'], false)) { - $class = new ReflectionClass($arguments['printer']); - - if ($class->isSubclassOf(ResultPrinter::class)) { - $printerClass = $arguments['printer']; - } - } - - $this->printer = new $printerClass( - (isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null, - $arguments['verbose'], - $arguments['colors'], - $arguments['debug'], - $arguments['columns'], - $arguments['reverseList'] - ); - - if (isset($originalExecutionOrder) && ($this->printer instanceof CliTestDoxPrinter)) { - /* @var CliTestDoxPrinter */ - $this->printer->setOriginalExecutionOrder($originalExecutionOrder); - } - } - } - - $this->printer->write( - Version::getVersionString() . "\n" - ); - - self::$versionStringPrinted = true; - - if ($arguments['verbose']) { - $runtime = $this->runtime->getNameWithVersion(); - - if ($this->runtime->hasXdebug()) { - $runtime .= \sprintf( - ' with Xdebug %s', - \phpversion('xdebug') - ); - } - - $this->writeMessage('Runtime', $runtime); - - if (isset($arguments['configuration'])) { - $this->writeMessage( - 'Configuration', - $arguments['configuration']->getFilename() - ); - } - - foreach ($arguments['loadedExtensions'] as $extension) { - $this->writeMessage( - 'Extension', - $extension - ); - } - - foreach ($arguments['notLoadedExtensions'] as $extension) { - $this->writeMessage( - 'Extension', - $extension - ); - } - } - - if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) { - $this->writeMessage( - 'Random seed', - $arguments['randomOrderSeed'] - ); - } - - if (isset($tooFewColumnsRequested)) { - $this->writeMessage('Error', 'Less than 16 columns requested, number of columns set to 16'); - } - - if ($this->runtime->discardsComments()) { - $this->writeMessage('Warning', 'opcache.save_comments=0 set; annotations will not work'); - } - - if (isset($arguments['configuration']) && $arguments['configuration']->hasValidationErrors()) { - $this->write( - "\n Warning - The configuration file did not pass validation!\n The following problems have been detected:\n" - ); - - foreach ($arguments['configuration']->getValidationErrors() as $line => $errors) { - $this->write(\sprintf("\n Line %d:\n", $line)); - - foreach ($errors as $msg) { - $this->write(\sprintf(" - %s\n", $msg)); - } - } - $this->write("\n Test results may not be as expected.\n\n"); - } - - foreach ($arguments['listeners'] as $listener) { - $result->addListener($listener); - } - - $result->addListener($this->printer); - - $codeCoverageReports = 0; - - if (!isset($arguments['noLogging'])) { - if (isset($arguments['testdoxHTMLFile'])) { - $result->addListener( - new HtmlResultPrinter( - $arguments['testdoxHTMLFile'], - $arguments['testdoxGroups'], - $arguments['testdoxExcludeGroups'] - ) - ); - } - - if (isset($arguments['testdoxTextFile'])) { - $result->addListener( - new TextResultPrinter( - $arguments['testdoxTextFile'], - $arguments['testdoxGroups'], - $arguments['testdoxExcludeGroups'] - ) - ); - } - - if (isset($arguments['testdoxXMLFile'])) { - $result->addListener( - new XmlResultPrinter( - $arguments['testdoxXMLFile'] - ) - ); - } - - if (isset($arguments['teamcityLogfile'])) { - $result->addListener( - new TeamCity($arguments['teamcityLogfile']) - ); - } - - if (isset($arguments['junitLogfile'])) { - $result->addListener( - new JUnit( - $arguments['junitLogfile'], - $arguments['reportUselessTests'] - ) - ); - } - - if (isset($arguments['coverageClover'])) { - $codeCoverageReports++; - } - - if (isset($arguments['coverageCrap4J'])) { - $codeCoverageReports++; - } - - if (isset($arguments['coverageHtml'])) { - $codeCoverageReports++; - } - - if (isset($arguments['coveragePHP'])) { - $codeCoverageReports++; - } - - if (isset($arguments['coverageText'])) { - $codeCoverageReports++; - } - - if (isset($arguments['coverageXml'])) { - $codeCoverageReports++; - } - } - - if (isset($arguments['noCoverage'])) { - $codeCoverageReports = 0; - } - - if ($codeCoverageReports > 0 && !$this->runtime->canCollectCodeCoverage()) { - $this->writeMessage('Error', 'No code coverage driver is available'); - - $codeCoverageReports = 0; - } - - if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) { - $whitelistFromConfigurationFile = false; - $whitelistFromOption = false; - - if (isset($arguments['whitelist'])) { - $this->codeCoverageFilter->addDirectoryToWhitelist($arguments['whitelist']); - - $whitelistFromOption = true; - } - - if (isset($arguments['configuration'])) { - $filterConfiguration = $arguments['configuration']->getFilterConfiguration(); - - if (!empty($filterConfiguration['whitelist'])) { - $whitelistFromConfigurationFile = true; - } - - if (!empty($filterConfiguration['whitelist'])) { - foreach ($filterConfiguration['whitelist']['include']['directory'] as $dir) { - $this->codeCoverageFilter->addDirectoryToWhitelist( - $dir['path'], - $dir['suffix'], - $dir['prefix'] - ); - } - - foreach ($filterConfiguration['whitelist']['include']['file'] as $file) { - $this->codeCoverageFilter->addFileToWhitelist($file); - } - - foreach ($filterConfiguration['whitelist']['exclude']['directory'] as $dir) { - $this->codeCoverageFilter->removeDirectoryFromWhitelist( - $dir['path'], - $dir['suffix'], - $dir['prefix'] - ); - } - - foreach ($filterConfiguration['whitelist']['exclude']['file'] as $file) { - $this->codeCoverageFilter->removeFileFromWhitelist($file); - } - } - } - } - - if ($codeCoverageReports > 0) { - $codeCoverage = new CodeCoverage( - null, - $this->codeCoverageFilter - ); - - $codeCoverage->setUnintentionallyCoveredSubclassesWhitelist( - [Comparator::class] - ); - - $codeCoverage->setCheckForUnintentionallyCoveredCode( - $arguments['strictCoverage'] - ); - - $codeCoverage->setCheckForMissingCoversAnnotation( - $arguments['strictCoverage'] - ); - - if (isset($arguments['forceCoversAnnotation'])) { - $codeCoverage->setForceCoversAnnotation( - $arguments['forceCoversAnnotation'] - ); - } - - if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { - $codeCoverage->setIgnoreDeprecatedCode( - $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] - ); - } - - if (isset($arguments['disableCodeCoverageIgnore'])) { - $codeCoverage->setDisableIgnoredLines(true); - } - - if (!empty($filterConfiguration['whitelist'])) { - $codeCoverage->setAddUncoveredFilesFromWhitelist( - $filterConfiguration['whitelist']['addUncoveredFilesFromWhitelist'] - ); - - $codeCoverage->setProcessUncoveredFilesFromWhitelist( - $filterConfiguration['whitelist']['processUncoveredFilesFromWhitelist'] - ); - } - - if (!$this->codeCoverageFilter->hasWhitelist()) { - if (!$whitelistFromConfigurationFile && !$whitelistFromOption) { - $this->writeMessage('Error', 'No whitelist is configured, no code coverage will be generated.'); - } else { - $this->writeMessage('Error', 'Incorrect whitelist config, no code coverage will be generated.'); - } - - $codeCoverageReports = 0; - - unset($codeCoverage); - } - } - - if (isset($arguments['xdebugFilterFile'], $filterConfiguration)) { - $this->write("\n"); - - $script = (new XdebugFilterScriptGenerator)->generate($filterConfiguration['whitelist']); - - if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !Filesystem::createDirectory(\dirname($arguments['xdebugFilterFile']))) { - $this->write(\sprintf('Cannot write Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile'])); - - exit(self::EXCEPTION_EXIT); - } - - \file_put_contents($arguments['xdebugFilterFile'], $script); - - $this->write(\sprintf('Wrote Xdebug filter script to %s ' . \PHP_EOL, $arguments['xdebugFilterFile'])); - - exit(self::SUCCESS_EXIT); - } - - $this->printer->write("\n"); - - if (isset($codeCoverage)) { - $result->setCodeCoverage($codeCoverage); - - if ($codeCoverageReports > 1 && isset($arguments['cacheTokens'])) { - $codeCoverage->setCacheTokens($arguments['cacheTokens']); - } - } - - $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']); - $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']); - $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']); - $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']); - - if ($arguments['enforceTimeLimit'] === true) { - if (!\class_exists(Invoker::class)) { - $this->writeMessage('Error', 'Package phpunit/php-invoker is required for enforcing time limits'); - } - - if (!\extension_loaded('pcntl') || \strpos(\ini_get('disable_functions'), 'pcntl') !== false) { - $this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits'); - } - } - $result->enforceTimeLimit($arguments['enforceTimeLimit']); - $result->setDefaultTimeLimit($arguments['defaultTimeLimit']); - $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']); - $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']); - $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']); - - if ($suite instanceof TestSuite) { - $this->processSuiteFilters($suite, $arguments); - $suite->setRunTestInSeparateProcess($arguments['processIsolation']); - } - - foreach ($this->extensions as $extension) { - if ($extension instanceof BeforeFirstTestHook) { - $extension->executeBeforeFirstTest(); - } - } - - $suite->run($result); - - foreach ($this->extensions as $extension) { - if ($extension instanceof AfterLastTestHook) { - $extension->executeAfterLastTest(); - } - } - - $result->flushListeners(); - - if ($this->printer instanceof ResultPrinter) { - $this->printer->printResult($result); - } - - if (isset($codeCoverage)) { - if (isset($arguments['coverageClover'])) { - $this->printer->write( - "\nGenerating code coverage report in Clover XML format ..." - ); - - try { - $writer = new CloverReport; - $writer->process($codeCoverage, $arguments['coverageClover']); - - $this->printer->write(" done\n"); - unset($writer); - } catch (CodeCoverageException $e) { - $this->printer->write( - " failed\n" . $e->getMessage() . "\n" - ); - } - } - - if (isset($arguments['coverageCrap4J'])) { - $this->printer->write( - "\nGenerating Crap4J report XML file ..." - ); - - try { - $writer = new Crap4jReport($arguments['crap4jThreshold']); - $writer->process($codeCoverage, $arguments['coverageCrap4J']); - - $this->printer->write(" done\n"); - unset($writer); - } catch (CodeCoverageException $e) { - $this->printer->write( - " failed\n" . $e->getMessage() . "\n" - ); - } - } - - if (isset($arguments['coverageHtml'])) { - $this->printer->write( - "\nGenerating code coverage report in HTML format ..." - ); - - try { - $writer = new HtmlReport( - $arguments['reportLowUpperBound'], - $arguments['reportHighLowerBound'], - \sprintf( - ' and PHPUnit %s', - Version::id() - ) - ); - - $writer->process($codeCoverage, $arguments['coverageHtml']); - - $this->printer->write(" done\n"); - unset($writer); - } catch (CodeCoverageException $e) { - $this->printer->write( - " failed\n" . $e->getMessage() . "\n" - ); - } - } - - if (isset($arguments['coveragePHP'])) { - $this->printer->write( - "\nGenerating code coverage report in PHP format ..." - ); - - try { - $writer = new PhpReport; - $writer->process($codeCoverage, $arguments['coveragePHP']); - - $this->printer->write(" done\n"); - unset($writer); - } catch (CodeCoverageException $e) { - $this->printer->write( - " failed\n" . $e->getMessage() . "\n" - ); - } - } - - if (isset($arguments['coverageText'])) { - if ($arguments['coverageText'] == 'php://stdout') { - $outputStream = $this->printer; - $colors = $arguments['colors'] && $arguments['colors'] != ResultPrinter::COLOR_NEVER; - } else { - $outputStream = new Printer($arguments['coverageText']); - $colors = false; - } - - $processor = new TextReport( - $arguments['reportLowUpperBound'], - $arguments['reportHighLowerBound'], - $arguments['coverageTextShowUncoveredFiles'], - $arguments['coverageTextShowOnlySummary'] - ); - - $outputStream->write( - $processor->process($codeCoverage, $colors) - ); - } - - if (isset($arguments['coverageXml'])) { - $this->printer->write( - "\nGenerating code coverage report in PHPUnit XML format ..." - ); - - try { - $writer = new XmlReport(Version::id()); - $writer->process($codeCoverage, $arguments['coverageXml']); - - $this->printer->write(" done\n"); - unset($writer); - } catch (CodeCoverageException $e) { - $this->printer->write( - " failed\n" . $e->getMessage() . "\n" - ); - } - } - } - - if ($exit) { - if ($result->wasSuccessfulIgnoringWarnings()) { - if ($arguments['failOnRisky'] && !$result->allHarmless()) { - exit(self::FAILURE_EXIT); - } - - if ($arguments['failOnWarning'] && $result->warningCount() > 0) { - exit(self::FAILURE_EXIT); - } - - exit(self::SUCCESS_EXIT); - } - - if ($result->errorCount() > 0) { - exit(self::EXCEPTION_EXIT); - } - - if ($result->failureCount() > 0) { - exit(self::FAILURE_EXIT); - } - } - - return $result; - } - - public function setPrinter(ResultPrinter $resultPrinter): void - { - $this->printer = $resultPrinter; - } - - /** - * Returns the loader to be used. - */ - public function getLoader(): TestSuiteLoader - { - if ($this->loader === null) { - $this->loader = new StandardTestSuiteLoader; - } - - return $this->loader; - } - - protected function createTestResult(): TestResult - { - return new TestResult; - } - - /** - * Override to define how to handle a failed loading of - * a test suite. - */ - protected function runFailed(string $message): void - { - $this->write($message . \PHP_EOL); - - exit(self::FAILURE_EXIT); - } - - protected function write(string $buffer): void - { - if (\PHP_SAPI != 'cli' && \PHP_SAPI != 'phpdbg') { - $buffer = \htmlspecialchars($buffer); - } - - if ($this->printer !== null) { - $this->printer->write($buffer); - } else { - print $buffer; - } - } - - /** - * @throws Exception - */ - protected function handleConfiguration(array &$arguments): void - { - if (isset($arguments['configuration']) && - !$arguments['configuration'] instanceof Configuration) { - $arguments['configuration'] = Configuration::getInstance( - $arguments['configuration'] - ); - } - - $arguments['debug'] = $arguments['debug'] ?? false; - $arguments['filter'] = $arguments['filter'] ?? false; - $arguments['listeners'] = $arguments['listeners'] ?? []; - - if (isset($arguments['configuration'])) { - $arguments['configuration']->handlePHPConfiguration(); - - $phpunitConfiguration = $arguments['configuration']->getPHPUnitConfiguration(); - - if (isset($phpunitConfiguration['backupGlobals']) && !isset($arguments['backupGlobals'])) { - $arguments['backupGlobals'] = $phpunitConfiguration['backupGlobals']; - } - - if (isset($phpunitConfiguration['backupStaticAttributes']) && !isset($arguments['backupStaticAttributes'])) { - $arguments['backupStaticAttributes'] = $phpunitConfiguration['backupStaticAttributes']; - } - - if (isset($phpunitConfiguration['beStrictAboutChangesToGlobalState']) && !isset($arguments['beStrictAboutChangesToGlobalState'])) { - $arguments['beStrictAboutChangesToGlobalState'] = $phpunitConfiguration['beStrictAboutChangesToGlobalState']; - } - - if (isset($phpunitConfiguration['bootstrap']) && !isset($arguments['bootstrap'])) { - $arguments['bootstrap'] = $phpunitConfiguration['bootstrap']; - } - - if (isset($phpunitConfiguration['cacheResult']) && !isset($arguments['cacheResult'])) { - $arguments['cacheResult'] = $phpunitConfiguration['cacheResult']; - } - - if (isset($phpunitConfiguration['cacheResultFile']) && !isset($arguments['cacheResultFile'])) { - $arguments['cacheResultFile'] = $phpunitConfiguration['cacheResultFile']; - } - - if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) { - $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; - } - - if (isset($phpunitConfiguration['cacheTokens']) && !isset($arguments['cacheTokens'])) { - $arguments['cacheTokens'] = $phpunitConfiguration['cacheTokens']; - } - - if (isset($phpunitConfiguration['colors']) && !isset($arguments['colors'])) { - $arguments['colors'] = $phpunitConfiguration['colors']; - } - - if (isset($phpunitConfiguration['convertDeprecationsToExceptions']) && !isset($arguments['convertDeprecationsToExceptions'])) { - $arguments['convertDeprecationsToExceptions'] = $phpunitConfiguration['convertDeprecationsToExceptions']; - } - - if (isset($phpunitConfiguration['convertErrorsToExceptions']) && !isset($arguments['convertErrorsToExceptions'])) { - $arguments['convertErrorsToExceptions'] = $phpunitConfiguration['convertErrorsToExceptions']; - } - - if (isset($phpunitConfiguration['convertNoticesToExceptions']) && !isset($arguments['convertNoticesToExceptions'])) { - $arguments['convertNoticesToExceptions'] = $phpunitConfiguration['convertNoticesToExceptions']; - } - - if (isset($phpunitConfiguration['convertWarningsToExceptions']) && !isset($arguments['convertWarningsToExceptions'])) { - $arguments['convertWarningsToExceptions'] = $phpunitConfiguration['convertWarningsToExceptions']; - } - - if (isset($phpunitConfiguration['processIsolation']) && !isset($arguments['processIsolation'])) { - $arguments['processIsolation'] = $phpunitConfiguration['processIsolation']; - } - - if (isset($phpunitConfiguration['stopOnDefect']) && !isset($arguments['stopOnDefect'])) { - $arguments['stopOnDefect'] = $phpunitConfiguration['stopOnDefect']; - } - - if (isset($phpunitConfiguration['stopOnError']) && !isset($arguments['stopOnError'])) { - $arguments['stopOnError'] = $phpunitConfiguration['stopOnError']; - } - - if (isset($phpunitConfiguration['stopOnFailure']) && !isset($arguments['stopOnFailure'])) { - $arguments['stopOnFailure'] = $phpunitConfiguration['stopOnFailure']; - } - - if (isset($phpunitConfiguration['stopOnWarning']) && !isset($arguments['stopOnWarning'])) { - $arguments['stopOnWarning'] = $phpunitConfiguration['stopOnWarning']; - } - - if (isset($phpunitConfiguration['stopOnIncomplete']) && !isset($arguments['stopOnIncomplete'])) { - $arguments['stopOnIncomplete'] = $phpunitConfiguration['stopOnIncomplete']; - } - - if (isset($phpunitConfiguration['stopOnRisky']) && !isset($arguments['stopOnRisky'])) { - $arguments['stopOnRisky'] = $phpunitConfiguration['stopOnRisky']; - } - - if (isset($phpunitConfiguration['stopOnSkipped']) && !isset($arguments['stopOnSkipped'])) { - $arguments['stopOnSkipped'] = $phpunitConfiguration['stopOnSkipped']; - } - - if (isset($phpunitConfiguration['failOnWarning']) && !isset($arguments['failOnWarning'])) { - $arguments['failOnWarning'] = $phpunitConfiguration['failOnWarning']; - } - - if (isset($phpunitConfiguration['failOnRisky']) && !isset($arguments['failOnRisky'])) { - $arguments['failOnRisky'] = $phpunitConfiguration['failOnRisky']; - } - - if (isset($phpunitConfiguration['timeoutForSmallTests']) && !isset($arguments['timeoutForSmallTests'])) { - $arguments['timeoutForSmallTests'] = $phpunitConfiguration['timeoutForSmallTests']; - } - - if (isset($phpunitConfiguration['timeoutForMediumTests']) && !isset($arguments['timeoutForMediumTests'])) { - $arguments['timeoutForMediumTests'] = $phpunitConfiguration['timeoutForMediumTests']; - } - - if (isset($phpunitConfiguration['timeoutForLargeTests']) && !isset($arguments['timeoutForLargeTests'])) { - $arguments['timeoutForLargeTests'] = $phpunitConfiguration['timeoutForLargeTests']; - } - - if (isset($phpunitConfiguration['reportUselessTests']) && !isset($arguments['reportUselessTests'])) { - $arguments['reportUselessTests'] = $phpunitConfiguration['reportUselessTests']; - } - - if (isset($phpunitConfiguration['strictCoverage']) && !isset($arguments['strictCoverage'])) { - $arguments['strictCoverage'] = $phpunitConfiguration['strictCoverage']; - } - - if (isset($phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage']) && !isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) { - $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $phpunitConfiguration['ignoreDeprecatedCodeUnitsFromCodeCoverage']; - } - - if (isset($phpunitConfiguration['disallowTestOutput']) && !isset($arguments['disallowTestOutput'])) { - $arguments['disallowTestOutput'] = $phpunitConfiguration['disallowTestOutput']; - } - - if (isset($phpunitConfiguration['defaultTimeLimit']) && !isset($arguments['defaultTimeLimit'])) { - $arguments['defaultTimeLimit'] = $phpunitConfiguration['defaultTimeLimit']; - } - - if (isset($phpunitConfiguration['enforceTimeLimit']) && !isset($arguments['enforceTimeLimit'])) { - $arguments['enforceTimeLimit'] = $phpunitConfiguration['enforceTimeLimit']; - } - - if (isset($phpunitConfiguration['disallowTodoAnnotatedTests']) && !isset($arguments['disallowTodoAnnotatedTests'])) { - $arguments['disallowTodoAnnotatedTests'] = $phpunitConfiguration['disallowTodoAnnotatedTests']; - } - - if (isset($phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']) && !isset($arguments['beStrictAboutResourceUsageDuringSmallTests'])) { - $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $phpunitConfiguration['beStrictAboutResourceUsageDuringSmallTests']; - } - - if (isset($phpunitConfiguration['verbose']) && !isset($arguments['verbose'])) { - $arguments['verbose'] = $phpunitConfiguration['verbose']; - } - - if (isset($phpunitConfiguration['reverseDefectList']) && !isset($arguments['reverseList'])) { - $arguments['reverseList'] = $phpunitConfiguration['reverseDefectList']; - } - - if (isset($phpunitConfiguration['forceCoversAnnotation']) && !isset($arguments['forceCoversAnnotation'])) { - $arguments['forceCoversAnnotation'] = $phpunitConfiguration['forceCoversAnnotation']; - } - - if (isset($phpunitConfiguration['disableCodeCoverageIgnore']) && !isset($arguments['disableCodeCoverageIgnore'])) { - $arguments['disableCodeCoverageIgnore'] = $phpunitConfiguration['disableCodeCoverageIgnore']; - } - - if (isset($phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']) && !isset($arguments['registerMockObjectsFromTestArgumentsRecursively'])) { - $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $phpunitConfiguration['registerMockObjectsFromTestArgumentsRecursively']; - } - - if (isset($phpunitConfiguration['executionOrder']) && !isset($arguments['executionOrder'])) { - $arguments['executionOrder'] = $phpunitConfiguration['executionOrder']; - } - - if (isset($phpunitConfiguration['executionOrderDefects']) && !isset($arguments['executionOrderDefects'])) { - $arguments['executionOrderDefects'] = $phpunitConfiguration['executionOrderDefects']; - } - - if (isset($phpunitConfiguration['resolveDependencies']) && !isset($arguments['resolveDependencies'])) { - $arguments['resolveDependencies'] = $phpunitConfiguration['resolveDependencies']; - } - - $groupCliArgs = []; - - if (!empty($arguments['groups'])) { - $groupCliArgs = $arguments['groups']; - } - - $groupConfiguration = $arguments['configuration']->getGroupConfiguration(); - - if (!empty($groupConfiguration['include']) && !isset($arguments['groups'])) { - $arguments['groups'] = $groupConfiguration['include']; - } - - if (!empty($groupConfiguration['exclude']) && !isset($arguments['excludeGroups'])) { - $arguments['excludeGroups'] = \array_diff($groupConfiguration['exclude'], $groupCliArgs); - } - - foreach ($arguments['configuration']->getExtensionConfiguration() as $extension) { - if (!\class_exists($extension['class'], false) && $extension['file'] !== '') { - require_once $extension['file']; - } - - if (!\class_exists($extension['class'])) { - throw new Exception( - \sprintf( - 'Class "%s" does not exist', - $extension['class'] - ) - ); - } - - $extensionClass = new ReflectionClass($extension['class']); - - if (!$extensionClass->implementsInterface(Hook::class)) { - throw new Exception( - \sprintf( - 'Class "%s" does not implement a PHPUnit\Runner\Hook interface', - $extension['class'] - ) - ); - } - - if (\count($extension['arguments']) == 0) { - $this->extensions[] = $extensionClass->newInstance(); - } else { - $this->extensions[] = $extensionClass->newInstanceArgs( - $extension['arguments'] - ); - } - } - - foreach ($arguments['configuration']->getListenerConfiguration() as $listener) { - if (!\class_exists($listener['class'], false) && - $listener['file'] !== '') { - require_once $listener['file']; - } - - if (!\class_exists($listener['class'])) { - throw new Exception( - \sprintf( - 'Class "%s" does not exist', - $listener['class'] - ) - ); - } - - $listenerClass = new ReflectionClass($listener['class']); - - if (!$listenerClass->implementsInterface(TestListener::class)) { - throw new Exception( - \sprintf( - 'Class "%s" does not implement the PHPUnit\Framework\TestListener interface', - $listener['class'] - ) - ); - } - - if (\count($listener['arguments']) == 0) { - $listener = new $listener['class']; - } else { - $listener = $listenerClass->newInstanceArgs( - $listener['arguments'] - ); - } - - $arguments['listeners'][] = $listener; - } - - $loggingConfiguration = $arguments['configuration']->getLoggingConfiguration(); - - if (isset($loggingConfiguration['coverage-clover']) && !isset($arguments['coverageClover'])) { - $arguments['coverageClover'] = $loggingConfiguration['coverage-clover']; - } - - if (isset($loggingConfiguration['coverage-crap4j']) && !isset($arguments['coverageCrap4J'])) { - $arguments['coverageCrap4J'] = $loggingConfiguration['coverage-crap4j']; - - if (isset($loggingConfiguration['crap4jThreshold']) && !isset($arguments['crap4jThreshold'])) { - $arguments['crap4jThreshold'] = $loggingConfiguration['crap4jThreshold']; - } - } - - if (isset($loggingConfiguration['coverage-html']) && !isset($arguments['coverageHtml'])) { - if (isset($loggingConfiguration['lowUpperBound']) && !isset($arguments['reportLowUpperBound'])) { - $arguments['reportLowUpperBound'] = $loggingConfiguration['lowUpperBound']; - } - - if (isset($loggingConfiguration['highLowerBound']) && !isset($arguments['reportHighLowerBound'])) { - $arguments['reportHighLowerBound'] = $loggingConfiguration['highLowerBound']; - } - - $arguments['coverageHtml'] = $loggingConfiguration['coverage-html']; - } - - if (isset($loggingConfiguration['coverage-php']) && !isset($arguments['coveragePHP'])) { - $arguments['coveragePHP'] = $loggingConfiguration['coverage-php']; - } - - if (isset($loggingConfiguration['coverage-text']) && !isset($arguments['coverageText'])) { - $arguments['coverageText'] = $loggingConfiguration['coverage-text']; - - if (isset($loggingConfiguration['coverageTextShowUncoveredFiles'])) { - $arguments['coverageTextShowUncoveredFiles'] = $loggingConfiguration['coverageTextShowUncoveredFiles']; - } else { - $arguments['coverageTextShowUncoveredFiles'] = false; - } - - if (isset($loggingConfiguration['coverageTextShowOnlySummary'])) { - $arguments['coverageTextShowOnlySummary'] = $loggingConfiguration['coverageTextShowOnlySummary']; - } else { - $arguments['coverageTextShowOnlySummary'] = false; - } - } - - if (isset($loggingConfiguration['coverage-xml']) && !isset($arguments['coverageXml'])) { - $arguments['coverageXml'] = $loggingConfiguration['coverage-xml']; - } - - if (isset($loggingConfiguration['plain'])) { - $arguments['listeners'][] = new ResultPrinter( - $loggingConfiguration['plain'], - true - ); - } - - if (isset($loggingConfiguration['teamcity']) && !isset($arguments['teamcityLogfile'])) { - $arguments['teamcityLogfile'] = $loggingConfiguration['teamcity']; - } - - if (isset($loggingConfiguration['junit']) && !isset($arguments['junitLogfile'])) { - $arguments['junitLogfile'] = $loggingConfiguration['junit']; - } - - if (isset($loggingConfiguration['testdox-html']) && !isset($arguments['testdoxHTMLFile'])) { - $arguments['testdoxHTMLFile'] = $loggingConfiguration['testdox-html']; - } - - if (isset($loggingConfiguration['testdox-text']) && !isset($arguments['testdoxTextFile'])) { - $arguments['testdoxTextFile'] = $loggingConfiguration['testdox-text']; - } - - if (isset($loggingConfiguration['testdox-xml']) && !isset($arguments['testdoxXMLFile'])) { - $arguments['testdoxXMLFile'] = $loggingConfiguration['testdox-xml']; - } - - $testdoxGroupConfiguration = $arguments['configuration']->getTestdoxGroupConfiguration(); - - if (isset($testdoxGroupConfiguration['include']) && - !isset($arguments['testdoxGroups'])) { - $arguments['testdoxGroups'] = $testdoxGroupConfiguration['include']; - } - - if (isset($testdoxGroupConfiguration['exclude']) && - !isset($arguments['testdoxExcludeGroups'])) { - $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration['exclude']; - } - } - - $arguments['addUncoveredFilesFromWhitelist'] = $arguments['addUncoveredFilesFromWhitelist'] ?? true; - $arguments['backupGlobals'] = $arguments['backupGlobals'] ?? null; - $arguments['backupStaticAttributes'] = $arguments['backupStaticAttributes'] ?? null; - $arguments['beStrictAboutChangesToGlobalState'] = $arguments['beStrictAboutChangesToGlobalState'] ?? null; - $arguments['beStrictAboutResourceUsageDuringSmallTests'] = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? false; - $arguments['cacheResult'] = $arguments['cacheResult'] ?? false; - $arguments['cacheTokens'] = $arguments['cacheTokens'] ?? false; - $arguments['colors'] = $arguments['colors'] ?? ResultPrinter::COLOR_DEFAULT; - $arguments['columns'] = $arguments['columns'] ?? 80; - $arguments['convertDeprecationsToExceptions'] = $arguments['convertDeprecationsToExceptions'] ?? true; - $arguments['convertErrorsToExceptions'] = $arguments['convertErrorsToExceptions'] ?? true; - $arguments['convertNoticesToExceptions'] = $arguments['convertNoticesToExceptions'] ?? true; - $arguments['convertWarningsToExceptions'] = $arguments['convertWarningsToExceptions'] ?? true; - $arguments['crap4jThreshold'] = $arguments['crap4jThreshold'] ?? 30; - $arguments['disallowTestOutput'] = $arguments['disallowTestOutput'] ?? false; - $arguments['disallowTodoAnnotatedTests'] = $arguments['disallowTodoAnnotatedTests'] ?? false; - $arguments['defaultTimeLimit'] = $arguments['defaultTimeLimit'] ?? 0; - $arguments['enforceTimeLimit'] = $arguments['enforceTimeLimit'] ?? false; - $arguments['excludeGroups'] = $arguments['excludeGroups'] ?? []; - $arguments['failOnRisky'] = $arguments['failOnRisky'] ?? false; - $arguments['failOnWarning'] = $arguments['failOnWarning'] ?? false; - $arguments['executionOrderDefects'] = $arguments['executionOrderDefects'] ?? TestSuiteSorter::ORDER_DEFAULT; - $arguments['groups'] = $arguments['groups'] ?? []; - $arguments['processIsolation'] = $arguments['processIsolation'] ?? false; - $arguments['processUncoveredFilesFromWhitelist'] = $arguments['processUncoveredFilesFromWhitelist'] ?? false; - $arguments['randomOrderSeed'] = $arguments['randomOrderSeed'] ?? \time(); - $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? false; - $arguments['repeat'] = $arguments['repeat'] ?? false; - $arguments['reportHighLowerBound'] = $arguments['reportHighLowerBound'] ?? 90; - $arguments['reportLowUpperBound'] = $arguments['reportLowUpperBound'] ?? 50; - $arguments['reportUselessTests'] = $arguments['reportUselessTests'] ?? true; - $arguments['reverseList'] = $arguments['reverseList'] ?? false; - $arguments['executionOrder'] = $arguments['executionOrder'] ?? TestSuiteSorter::ORDER_DEFAULT; - $arguments['resolveDependencies'] = $arguments['resolveDependencies'] ?? false; - $arguments['stopOnError'] = $arguments['stopOnError'] ?? false; - $arguments['stopOnFailure'] = $arguments['stopOnFailure'] ?? false; - $arguments['stopOnIncomplete'] = $arguments['stopOnIncomplete'] ?? false; - $arguments['stopOnRisky'] = $arguments['stopOnRisky'] ?? false; - $arguments['stopOnSkipped'] = $arguments['stopOnSkipped'] ?? false; - $arguments['stopOnWarning'] = $arguments['stopOnWarning'] ?? false; - $arguments['stopOnDefect'] = $arguments['stopOnDefect'] ?? false; - $arguments['strictCoverage'] = $arguments['strictCoverage'] ?? false; - $arguments['testdoxExcludeGroups'] = $arguments['testdoxExcludeGroups'] ?? []; - $arguments['testdoxGroups'] = $arguments['testdoxGroups'] ?? []; - $arguments['timeoutForLargeTests'] = $arguments['timeoutForLargeTests'] ?? 60; - $arguments['timeoutForMediumTests'] = $arguments['timeoutForMediumTests'] ?? 10; - $arguments['timeoutForSmallTests'] = $arguments['timeoutForSmallTests'] ?? 1; - $arguments['verbose'] = $arguments['verbose'] ?? false; - } - - /** - * @throws \ReflectionException - * @throws \InvalidArgumentException - */ - private function processSuiteFilters(TestSuite $suite, array $arguments): void - { - if (!$arguments['filter'] && - empty($arguments['groups']) && - empty($arguments['excludeGroups'])) { - return; - } - - $filterFactory = new Factory; - - if (!empty($arguments['excludeGroups'])) { - $filterFactory->addFilter( - new ReflectionClass(ExcludeGroupFilterIterator::class), - $arguments['excludeGroups'] - ); - } - - if (!empty($arguments['groups'])) { - $filterFactory->addFilter( - new ReflectionClass(IncludeGroupFilterIterator::class), - $arguments['groups'] - ); - } - - if ($arguments['filter']) { - $filterFactory->addFilter( - new ReflectionClass(NameFilterIterator::class), - $arguments['filter'] - ); - } - - $suite->injectFilter($filterFactory); - } - - private function writeMessage(string $type, string $message): void - { - if (!$this->messagePrinted) { - $this->write("\n"); - } - - $this->write( - \sprintf( - "%-15s%s\n", - $type . ':', - $message - ) - ); - - $this->messagePrinted = true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\TextUI; - -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestResult; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Util\InvalidArgumentHelper; -use PHPUnit\Util\Printer; -use SebastianBergmann\Environment\Console; -use SebastianBergmann\Timer\Timer; - -/** - * Prints the result of a TextUI TestRunner run. - */ -class ResultPrinter extends Printer implements TestListener -{ - public const EVENT_TEST_START = 0; - - public const EVENT_TEST_END = 1; - - public const EVENT_TESTSUITE_START = 2; - - public const EVENT_TESTSUITE_END = 3; - - public const COLOR_NEVER = 'never'; - - public const COLOR_AUTO = 'auto'; - - public const COLOR_ALWAYS = 'always'; - - public const COLOR_DEFAULT = self::COLOR_NEVER; - - private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; - - /** - * @var array - */ - private static $ansiCodes = [ - 'bold' => 1, - 'fg-black' => 30, - 'fg-red' => 31, - 'fg-green' => 32, - 'fg-yellow' => 33, - 'fg-blue' => 34, - 'fg-magenta' => 35, - 'fg-cyan' => 36, - 'fg-white' => 37, - 'bg-black' => 40, - 'bg-red' => 41, - 'bg-green' => 42, - 'bg-yellow' => 43, - 'bg-blue' => 44, - 'bg-magenta' => 45, - 'bg-cyan' => 46, - 'bg-white' => 47, - ]; - - /** - * @var int - */ - protected $column = 0; - - /** - * @var int - */ - protected $maxColumn; - - /** - * @var bool - */ - protected $lastTestFailed = false; - - /** - * @var int - */ - protected $numAssertions = 0; - - /** - * @var int - */ - protected $numTests = -1; - - /** - * @var int - */ - protected $numTestsRun = 0; - - /** - * @var int - */ - protected $numTestsWidth; - - /** - * @var bool - */ - protected $colors = false; - - /** - * @var bool - */ - protected $debug = false; - - /** - * @var bool - */ - protected $verbose = false; - - /** - * @var int - */ - private $numberOfColumns; - - /** - * @var bool - */ - private $reverse; - - /** - * @var bool - */ - private $defectListPrinted = false; - - /** - * Constructor. - * - * @param string $colors - * @param int|string $numberOfColumns - * @param null|mixed $out - * - * @throws Exception - */ - public function __construct($out = null, bool $verbose = false, $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false) - { - parent::__construct($out); - - if (!\in_array($colors, self::AVAILABLE_COLORS, true)) { - throw InvalidArgumentHelper::factory( - 3, - \vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS) - ); - } - - if (!\is_int($numberOfColumns) && $numberOfColumns !== 'max') { - throw InvalidArgumentHelper::factory(5, 'integer or "max"'); - } - - $console = new Console; - $maxNumberOfColumns = $console->getNumberOfColumns(); - - if ($numberOfColumns === 'max' || ($numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns)) { - $numberOfColumns = $maxNumberOfColumns; - } - - $this->numberOfColumns = $numberOfColumns; - $this->verbose = $verbose; - $this->debug = $debug; - $this->reverse = $reverse; - - if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) { - $this->colors = true; - } else { - $this->colors = (self::COLOR_ALWAYS === $colors); - } - } - - public function printResult(TestResult $result): void - { - $this->printHeader(); - $this->printErrors($result); - $this->printWarnings($result); - $this->printFailures($result); - $this->printRisky($result); - - if ($this->verbose) { - $this->printIncompletes($result); - $this->printSkipped($result); - } - - $this->printFooter($result); - } - - /** - * An error occurred. - */ - public function addError(Test $test, \Throwable $t, float $time): void - { - $this->writeProgressWithColor('fg-red, bold', 'E'); - $this->lastTestFailed = true; - } - - /** - * A failure occurred. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - $this->writeProgressWithColor('bg-red, fg-white', 'F'); - $this->lastTestFailed = true; - } - - /** - * A warning occurred. - */ - public function addWarning(Test $test, Warning $e, float $time): void - { - $this->writeProgressWithColor('fg-yellow, bold', 'W'); - $this->lastTestFailed = true; - } - - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - $this->writeProgressWithColor('fg-yellow, bold', 'I'); - $this->lastTestFailed = true; - } - - /** - * Risky test. - */ - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - $this->writeProgressWithColor('fg-yellow, bold', 'R'); - $this->lastTestFailed = true; - } - - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - $this->writeProgressWithColor('fg-cyan, bold', 'S'); - $this->lastTestFailed = true; - } - - /** - * A testsuite started. - */ - public function startTestSuite(TestSuite $suite): void - { - if ($this->numTests == -1) { - $this->numTests = \count($suite); - $this->numTestsWidth = \strlen((string) $this->numTests); - $this->maxColumn = $this->numberOfColumns - \strlen(' / (XXX%)') - (2 * $this->numTestsWidth); - } - } - - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite): void - { - } - - /** - * A test started. - */ - public function startTest(Test $test): void - { - if ($this->debug) { - $this->write( - \sprintf( - "Test '%s' started\n", - \PHPUnit\Util\Test::describeAsString($test) - ) - ); - } - } - - /** - * A test ended. - */ - public function endTest(Test $test, float $time): void - { - if ($this->debug) { - $this->write( - \sprintf( - "Test '%s' ended\n", - \PHPUnit\Util\Test::describeAsString($test) - ) - ); - } - - if (!$this->lastTestFailed) { - $this->writeProgress('.'); - } - - if ($test instanceof TestCase) { - $this->numAssertions += $test->getNumAssertions(); - } elseif ($test instanceof PhptTestCase) { - $this->numAssertions++; - } - - $this->lastTestFailed = false; - - if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) { - $this->write($test->getActualOutput()); - } - } - - protected function printDefects(array $defects, string $type): void - { - $count = \count($defects); - - if ($count == 0) { - return; - } - - if ($this->defectListPrinted) { - $this->write("\n--\n\n"); - } - - $this->write( - \sprintf( - "There %s %d %s%s:\n", - ($count == 1) ? 'was' : 'were', - $count, - $type, - ($count == 1) ? '' : 's' - ) - ); - - $i = 1; - - if ($this->reverse) { - $defects = \array_reverse($defects); - } - - foreach ($defects as $defect) { - $this->printDefect($defect, $i++); - } - - $this->defectListPrinted = true; - } - - protected function printDefect(TestFailure $defect, int $count): void - { - $this->printDefectHeader($defect, $count); - $this->printDefectTrace($defect); - } - - protected function printDefectHeader(TestFailure $defect, int $count): void - { - $this->write( - \sprintf( - "\n%d) %s\n", - $count, - $defect->getTestName() - ) - ); - } - - protected function printDefectTrace(TestFailure $defect): void - { - $e = $defect->thrownException(); - $this->write((string) $e); - - while ($e = $e->getPrevious()) { - $this->write("\nCaused by\n" . $e); - } - } - - protected function printErrors(TestResult $result): void - { - $this->printDefects($result->errors(), 'error'); - } - - protected function printFailures(TestResult $result): void - { - $this->printDefects($result->failures(), 'failure'); - } - - protected function printWarnings(TestResult $result): void - { - $this->printDefects($result->warnings(), 'warning'); - } - - protected function printIncompletes(TestResult $result): void - { - $this->printDefects($result->notImplemented(), 'incomplete test'); - } - - protected function printRisky(TestResult $result): void - { - $this->printDefects($result->risky(), 'risky test'); - } - - protected function printSkipped(TestResult $result): void - { - $this->printDefects($result->skipped(), 'skipped test'); - } - - protected function printHeader(): void - { - $this->write("\n\n" . Timer::resourceUsage() . "\n\n"); - } - - protected function printFooter(TestResult $result): void - { - if (\count($result) === 0) { - $this->writeWithColor( - 'fg-black, bg-yellow', - 'No tests executed!' - ); - - return; - } - - if ($result->wasSuccessful() && - $result->allHarmless() && - $result->allCompletelyImplemented() && - $result->noneSkipped()) { - $this->writeWithColor( - 'fg-black, bg-green', - \sprintf( - 'OK (%d test%s, %d assertion%s)', - \count($result), - (\count($result) == 1) ? '' : 's', - $this->numAssertions, - ($this->numAssertions == 1) ? '' : 's' - ) - ); - } else { - if ($result->wasSuccessful()) { - $color = 'fg-black, bg-yellow'; - - if ($this->verbose || !$result->allHarmless()) { - $this->write("\n"); - } - - $this->writeWithColor( - $color, - 'OK, but incomplete, skipped, or risky tests!' - ); - } else { - $this->write("\n"); - - if ($result->errorCount()) { - $color = 'fg-white, bg-red'; - - $this->writeWithColor( - $color, - 'ERRORS!' - ); - } elseif ($result->failureCount()) { - $color = 'fg-white, bg-red'; - - $this->writeWithColor( - $color, - 'FAILURES!' - ); - } elseif ($result->warningCount()) { - $color = 'fg-black, bg-yellow'; - - $this->writeWithColor( - $color, - 'WARNINGS!' - ); - } - } - - $this->writeCountString(\count($result), 'Tests', $color, true); - $this->writeCountString($this->numAssertions, 'Assertions', $color, true); - $this->writeCountString($result->errorCount(), 'Errors', $color); - $this->writeCountString($result->failureCount(), 'Failures', $color); - $this->writeCountString($result->warningCount(), 'Warnings', $color); - $this->writeCountString($result->skippedCount(), 'Skipped', $color); - $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color); - $this->writeCountString($result->riskyCount(), 'Risky', $color); - $this->writeWithColor($color, '.'); - } - } - - protected function writeProgress(string $progress): void - { - if ($this->debug) { - return; - } - - $this->write($progress); - $this->column++; - $this->numTestsRun++; - - if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) { - if ($this->numTestsRun == $this->numTests) { - $this->write(\str_repeat(' ', $this->maxColumn - $this->column)); - } - - $this->write( - \sprintf( - ' %' . $this->numTestsWidth . 'd / %' . - $this->numTestsWidth . 'd (%3s%%)', - $this->numTestsRun, - $this->numTests, - \floor(($this->numTestsRun / $this->numTests) * 100) - ) - ); - - if ($this->column == $this->maxColumn) { - $this->writeNewLine(); - } - } - } - - protected function writeNewLine(): void - { - $this->column = 0; - $this->write("\n"); - } - - /** - * Formats a buffer with a specified ANSI color sequence if colors are - * enabled. - */ - protected function formatWithColor(string $color, string $buffer): string - { - if (!$this->colors) { - return $buffer; - } - - $codes = \array_map('\trim', \explode(',', $color)); - $lines = \explode("\n", $buffer); - $padding = \max(\array_map('\strlen', $lines)); - $styles = []; - - foreach ($codes as $code) { - $styles[] = self::$ansiCodes[$code]; - } - - $style = \sprintf("\x1b[%sm", \implode(';', $styles)); - - $styledLines = []; - - foreach ($lines as $line) { - $styledLines[] = $style . \str_pad($line, $padding) . "\x1b[0m"; - } - - return \implode("\n", $styledLines); - } - - /** - * Writes a buffer out with a color sequence if colors are enabled. - */ - protected function writeWithColor(string $color, string $buffer, bool $lf = true): void - { - $this->write($this->formatWithColor($color, $buffer)); - - if ($lf) { - $this->write("\n"); - } - } - - /** - * Writes progress with a color sequence if colors are enabled. - */ - protected function writeProgressWithColor(string $color, string $buffer): void - { - $buffer = $this->formatWithColor($color, $buffer); - $this->writeProgress($buffer); - } - - private function writeCountString(int $count, string $name, string $color, bool $always = false): void - { - static $first = true; - - if ($always || $count > 0) { - $this->writeWithColor( - $color, - \sprintf( - '%s%s: %d', - !$first ? ', ' : '', - $name, - $count - ), - false - ); - - $first = false; - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -trait TestListenerDefaultImplementation -{ - public function addError(Test $test, \Throwable $t, float $time): void - { - } - - public function addWarning(Test $test, Warning $e, float $time): void - { - } - - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - } - - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - } - - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - } - - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - } - - public function startTestSuite(TestSuite $suite): void - { - } - - public function endTestSuite(TestSuite $suite): void - { - } - - public function startTest(Test $test): void - { - } - - public function endTest(Test $test, float $time): void - { - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class InvalidCoversTargetException extends CodeCoverageException -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -interface SkippedTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class OutputError extends AssertionFailedError -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * Thrown when an assertion failed. - */ -class AssertionFailedError extends Exception implements SelfDescribing -{ - /** - * Wrapper for getMessage() which is declared as final. - */ - public function toString(): string - { - return $this->getMessage(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class MissingCoversAnnotationException extends RiskyTestError -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class RiskyTestError extends AssertionFailedError implements RiskyTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -class Notice extends Error -{ - public static $enabled = true; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -class Deprecated extends Error -{ - public static $enabled = true; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -use PHPUnit\Framework\Exception; - -/** - * Wrapper for PHP errors. - */ -class Error extends Exception -{ - public function __construct(string $message, int $code, string $file, int $line, \Exception $previous = null) - { - parent::__construct($message, $code, $previous); - - $this->file = $file; - $this->line = $line; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Error; - -class Warning extends Error -{ - public static $enabled = true; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * A Listener for test progress. - */ -interface TestListener -{ - /** - * An error occurred. - */ - public function addError(Test $test, \Throwable $t, float $time): void; - - /** - * A warning occurred. - */ - public function addWarning(Test $test, Warning $e, float $time): void; - - /** - * A failure occurred. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void; - - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void; - - /** - * Risky test. - */ - public function addRiskyTest(Test $test, \Throwable $t, float $time): void; - - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void; - - /** - * A test suite started. - */ - public function startTestSuite(TestSuite $suite): void; - - /** - * A test suite ended. - */ - public function endTestSuite(TestSuite $suite): void; - - /** - * A test started. - */ - public function startTest(Test $test): void; - - /** - * A test ended. - */ - public function endTest(Test $test, float $time): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * A marker interface for marking any exception/error as result of an unit - * test as incomplete implementation or currently not implemented. - */ -interface IncompleteTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * Creates a synthetic failed assertion. - */ -class SyntheticError extends AssertionFailedError -{ - /** - * The synthetic file. - * - * @var string - */ - protected $syntheticFile = ''; - - /** - * The synthetic line number. - * - * @var int - */ - protected $syntheticLine = 0; - - /** - * The synthetic trace. - * - * @var array - */ - protected $syntheticTrace = []; - - public function __construct(string $message, int $code, string $file, int $line, array $trace) - { - parent::__construct($message, $code); - - $this->syntheticFile = $file; - $this->syntheticLine = $line; - $this->syntheticTrace = $trace; - } - - public function getSyntheticFile(): string - { - return $this->syntheticFile; - } - - public function getSyntheticLine(): int - { - return $this->syntheticLine; - } - - public function getSyntheticTrace(): array - { - return $this->syntheticTrace; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class IncompleteTestError extends AssertionFailedError implements IncompleteTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use ArrayAccess; -use Countable; -use DOMDocument; -use DOMElement; -use PHPUnit\Framework\Constraint\ArrayHasKey; -use PHPUnit\Framework\Constraint\ArraySubset; -use PHPUnit\Framework\Constraint\Attribute; -use PHPUnit\Framework\Constraint\Callback; -use PHPUnit\Framework\Constraint\ClassHasAttribute; -use PHPUnit\Framework\Constraint\ClassHasStaticAttribute; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\Count; -use PHPUnit\Framework\Constraint\DirectoryExists; -use PHPUnit\Framework\Constraint\FileExists; -use PHPUnit\Framework\Constraint\GreaterThan; -use PHPUnit\Framework\Constraint\IsAnything; -use PHPUnit\Framework\Constraint\IsEmpty; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\Constraint\IsFalse; -use PHPUnit\Framework\Constraint\IsFinite; -use PHPUnit\Framework\Constraint\IsIdentical; -use PHPUnit\Framework\Constraint\IsInfinite; -use PHPUnit\Framework\Constraint\IsInstanceOf; -use PHPUnit\Framework\Constraint\IsJson; -use PHPUnit\Framework\Constraint\IsNan; -use PHPUnit\Framework\Constraint\IsNull; -use PHPUnit\Framework\Constraint\IsReadable; -use PHPUnit\Framework\Constraint\IsTrue; -use PHPUnit\Framework\Constraint\IsType; -use PHPUnit\Framework\Constraint\IsWritable; -use PHPUnit\Framework\Constraint\JsonMatches; -use PHPUnit\Framework\Constraint\LessThan; -use PHPUnit\Framework\Constraint\LogicalAnd; -use PHPUnit\Framework\Constraint\LogicalNot; -use PHPUnit\Framework\Constraint\LogicalOr; -use PHPUnit\Framework\Constraint\LogicalXor; -use PHPUnit\Framework\Constraint\ObjectHasAttribute; -use PHPUnit\Framework\Constraint\RegularExpression; -use PHPUnit\Framework\Constraint\SameSize; -use PHPUnit\Framework\Constraint\StringContains; -use PHPUnit\Framework\Constraint\StringEndsWith; -use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; -use PHPUnit\Framework\Constraint\StringStartsWith; -use PHPUnit\Framework\Constraint\TraversableContains; -use PHPUnit\Framework\Constraint\TraversableContainsOnly; -use PHPUnit\Util\InvalidArgumentHelper; -use PHPUnit\Util\Type; -use PHPUnit\Util\Xml; -use ReflectionClass; -use ReflectionException; -use ReflectionObject; -use Traversable; - -/** - * A set of assertion methods. - */ -abstract class Assert -{ - /** - * @var int - */ - private static $count = 0; - - /** - * Asserts that an array has a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertArrayHasKey($key, $array, string $message = ''): void - { - if (!(\is_int($key) || \is_string($key))) { - throw InvalidArgumentHelper::factory( - 1, - 'integer or string' - ); - } - - if (!(\is_array($array) || $array instanceof ArrayAccess)) { - throw InvalidArgumentHelper::factory( - 2, - 'array or ArrayAccess' - ); - } - - $constraint = new ArrayHasKey($key); - - static::assertThat($array, $constraint, $message); - } - - /** - * Asserts that an array has a specified subset. - * - * @param array|ArrayAccess $subset - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494 - */ - public static function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void - { - if (!(\is_array($subset) || $subset instanceof ArrayAccess)) { - throw InvalidArgumentHelper::factory( - 1, - 'array or ArrayAccess' - ); - } - - if (!(\is_array($array) || $array instanceof ArrayAccess)) { - throw InvalidArgumentHelper::factory( - 2, - 'array or ArrayAccess' - ); - } - - $constraint = new ArraySubset($subset, $checkForObjectIdentity); - - static::assertThat($array, $constraint, $message); - } - - /** - * Asserts that an array does not have a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertArrayNotHasKey($key, $array, string $message = ''): void - { - if (!(\is_int($key) || \is_string($key))) { - throw InvalidArgumentHelper::factory( - 1, - 'integer or string' - ); - } - - if (!(\is_array($array) || $array instanceof ArrayAccess)) { - throw InvalidArgumentHelper::factory( - 2, - 'array or ArrayAccess' - ); - } - - $constraint = new LogicalNot( - new ArrayHasKey($key) - ); - - static::assertThat($array, $constraint, $message); - } - - /** - * Asserts that a haystack contains a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void - { - if (\is_array($haystack) || - (\is_object($haystack) && $haystack instanceof Traversable)) { - $constraint = new TraversableContains( - $needle, - $checkForObjectIdentity, - $checkForNonObjectIdentity - ); - } elseif (\is_string($haystack)) { - if (!\is_string($needle)) { - throw InvalidArgumentHelper::factory( - 1, - 'string' - ); - } - - $constraint = new StringContains( - $needle, - $ignoreCase - ); - } else { - throw InvalidArgumentHelper::factory( - 2, - 'array, traversable or string' - ); - } - - static::assertThat($haystack, $constraint, $message); - } - - /** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object contains a needle. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void - { - static::assertContains( - $needle, - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $message, - $ignoreCase, - $checkForObjectIdentity, - $checkForNonObjectIdentity - ); - } - - /** - * Asserts that a haystack does not contain a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void - { - if (\is_array($haystack) || - (\is_object($haystack) && $haystack instanceof Traversable)) { - $constraint = new LogicalNot( - new TraversableContains( - $needle, - $checkForObjectIdentity, - $checkForNonObjectIdentity - ) - ); - } elseif (\is_string($haystack)) { - if (!\is_string($needle)) { - throw InvalidArgumentHelper::factory( - 1, - 'string' - ); - } - - $constraint = new LogicalNot( - new StringContains( - $needle, - $ignoreCase - ) - ); - } else { - throw InvalidArgumentHelper::factory( - 2, - 'array, traversable or string' - ); - } - - static::assertThat($haystack, $constraint, $message); - } - - /** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object does not contain a needle. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void - { - static::assertNotContains( - $needle, - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $message, - $ignoreCase, - $checkForObjectIdentity, - $checkForNonObjectIdentity - ); - } - - /** - * Asserts that a haystack contains only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void - { - if ($isNativeType === null) { - $isNativeType = Type::isType($type); - } - - static::assertThat( - $haystack, - new TraversableContainsOnly( - $type, - $isNativeType - ), - $message - ); - } - - /** - * Asserts that a haystack contains only instances of a given class name. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void - { - static::assertThat( - $haystack, - new TraversableContainsOnly( - $className, - false - ), - $message - ); - } - - /** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object contains only values of a given type. - * - * @param object|string $haystackClassOrObject - * @param bool $isNativeType - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void - { - static::assertContainsOnly( - $type, - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $isNativeType, - $message - ); - } - - /** - * Asserts that a haystack does not contain only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void - { - if ($isNativeType === null) { - $isNativeType = Type::isType($type); - } - - static::assertThat( - $haystack, - new LogicalNot( - new TraversableContainsOnly( - $type, - $isNativeType - ) - ), - $message - ); - } - - /** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object does not contain only values of a given - * type. - * - * @param object|string $haystackClassOrObject - * @param bool $isNativeType - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void - { - static::assertNotContainsOnly( - $type, - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $isNativeType, - $message - ); - } - - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertCount(int $expectedCount, $haystack, string $message = ''): void - { - if (!$haystack instanceof Countable && !\is_iterable($haystack)) { - throw InvalidArgumentHelper::factory(2, 'countable or iterable'); - } - - static::assertThat( - $haystack, - new Count($expectedCount), - $message - ); - } - - /** - * Asserts the number of elements of an array, Countable or Traversable - * that is stored in an attribute. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void - { - static::assertCount( - $expectedCount, - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $message - ); - } - - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotCount(int $expectedCount, $haystack, string $message = ''): void - { - if (!$haystack instanceof Countable && !\is_iterable($haystack)) { - throw InvalidArgumentHelper::factory(2, 'countable or iterable'); - } - - $constraint = new LogicalNot( - new Count($expectedCount) - ); - - static::assertThat($haystack, $constraint, $message); - } - - /** - * Asserts the number of elements of an array, Countable or Traversable - * that is stored in an attribute. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void - { - static::assertNotCount( - $expectedCount, - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $message - ); - } - - /** - * Asserts that two variables are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void - { - $constraint = new IsEqual( - $expected, - $delta, - $maxDepth, - $canonicalize, - $ignoreCase - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that two variables are equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void - { - $constraint = new IsEqual( - $expected, - 0.0, - 10, - true, - false - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that two variables are equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void - { - $constraint = new IsEqual( - $expected, - 0.0, - 10, - false, - true - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that two variables are equal (with delta). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void - { - $constraint = new IsEqual( - $expected, - $delta - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that a variable is equal to an attribute of an object. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void - { - static::assertEquals( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message, - $delta, - $maxDepth, - $canonicalize, - $ignoreCase - ); - } - - /** - * Asserts that two variables are not equal. - * - * @param float $delta - * @param int $maxDepth - * @param bool $canonicalize - * @param bool $ignoreCase - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void - { - $constraint = new LogicalNot( - new IsEqual( - $expected, - $delta, - $maxDepth, - $canonicalize, - $ignoreCase - ) - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that two variables are not equal (canonicalizing). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void - { - $constraint = new LogicalNot( - new IsEqual( - $expected, - 0.0, - 10, - true, - false - ) - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that two variables are not equal (ignoring case). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void - { - $constraint = new LogicalNot( - new IsEqual( - $expected, - 0.0, - 10, - false, - true - ) - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that two variables are not equal (with delta). - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void - { - $constraint = new LogicalNot( - new IsEqual( - $expected, - $delta - ) - ); - - static::assertThat($actual, $constraint, $message); - } - - /** - * Asserts that a variable is not equal to an attribute of an object. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void - { - static::assertNotEquals( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message, - $delta, - $maxDepth, - $canonicalize, - $ignoreCase - ); - } - - /** - * Asserts that a variable is empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertEmpty($actual, string $message = ''): void - { - static::assertThat($actual, static::isEmpty(), $message); - } - - /** - * Asserts that a static attribute of a class or an attribute of an object - * is empty. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void - { - static::assertEmpty( - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $message - ); - } - - /** - * Asserts that a variable is not empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotEmpty($actual, string $message = ''): void - { - static::assertThat($actual, static::logicalNot(static::isEmpty()), $message); - } - - /** - * Asserts that a static attribute of a class or an attribute of an object - * is not empty. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void - { - static::assertNotEmpty( - static::readAttribute($haystackClassOrObject, $haystackAttributeName), - $message - ); - } - - /** - * Asserts that a value is greater than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertGreaterThan($expected, $actual, string $message = ''): void - { - static::assertThat($actual, static::greaterThan($expected), $message); - } - - /** - * Asserts that an attribute is greater than another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void - { - static::assertGreaterThan( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message - ); - } - - /** - * Asserts that a value is greater than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void - { - static::assertThat( - $actual, - static::greaterThanOrEqual($expected), - $message - ); - } - - /** - * Asserts that an attribute is greater than or equal to another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void - { - static::assertGreaterThanOrEqual( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message - ); - } - - /** - * Asserts that a value is smaller than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertLessThan($expected, $actual, string $message = ''): void - { - static::assertThat($actual, static::lessThan($expected), $message); - } - - /** - * Asserts that an attribute is smaller than another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void - { - static::assertLessThan( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message - ); - } - - /** - * Asserts that a value is smaller than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertLessThanOrEqual($expected, $actual, string $message = ''): void - { - static::assertThat($actual, static::lessThanOrEqual($expected), $message); - } - - /** - * Asserts that an attribute is smaller than or equal to another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void - { - static::assertLessThanOrEqual( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message - ); - } - - /** - * Asserts that the contents of one file is equal to the contents of another - * file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void - { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - - static::assertEquals( - \file_get_contents($expected), - \file_get_contents($actual), - $message, - 0, - 10, - $canonicalize, - $ignoreCase - ); - } - - /** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void - { - static::assertFileExists($expected, $message); - static::assertFileExists($actual, $message); - - static::assertNotEquals( - \file_get_contents($expected), - \file_get_contents($actual), - $message, - 0, - 10, - $canonicalize, - $ignoreCase - ); - } - - /** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void - { - static::assertFileExists($expectedFile, $message); - - /** @noinspection PhpUnitTestsInspection */ - static::assertEquals( - \file_get_contents($expectedFile), - $actualString, - $message, - 0, - 10, - $canonicalize, - $ignoreCase - ); - } - - /** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void - { - static::assertFileExists($expectedFile, $message); - - static::assertNotEquals( - \file_get_contents($expectedFile), - $actualString, - $message, - 0, - 10, - $canonicalize, - $ignoreCase - ); - } - - /** - * Asserts that a file/dir is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertIsReadable(string $filename, string $message = ''): void - { - static::assertThat($filename, new IsReadable, $message); - } - - /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotIsReadable(string $filename, string $message = ''): void - { - static::assertThat($filename, new LogicalNot(new IsReadable), $message); - } - - /** - * Asserts that a file/dir exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertIsWritable(string $filename, string $message = ''): void - { - static::assertThat($filename, new IsWritable, $message); - } - - /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotIsWritable(string $filename, string $message = ''): void - { - static::assertThat($filename, new LogicalNot(new IsWritable), $message); - } - - /** - * Asserts that a directory exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryExists(string $directory, string $message = ''): void - { - static::assertThat($directory, new DirectoryExists, $message); - } - - /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryNotExists(string $directory, string $message = ''): void - { - static::assertThat($directory, new LogicalNot(new DirectoryExists), $message); - } - - /** - * Asserts that a directory exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryIsReadable(string $directory, string $message = ''): void - { - self::assertDirectoryExists($directory, $message); - self::assertIsReadable($directory, $message); - } - - /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryNotIsReadable(string $directory, string $message = ''): void - { - self::assertDirectoryExists($directory, $message); - self::assertNotIsReadable($directory, $message); - } - - /** - * Asserts that a directory exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryIsWritable(string $directory, string $message = ''): void - { - self::assertDirectoryExists($directory, $message); - self::assertIsWritable($directory, $message); - } - - /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertDirectoryNotIsWritable(string $directory, string $message = ''): void - { - self::assertDirectoryExists($directory, $message); - self::assertNotIsWritable($directory, $message); - } - - /** - * Asserts that a file exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileExists(string $filename, string $message = ''): void - { - static::assertThat($filename, new FileExists, $message); - } - - /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotExists(string $filename, string $message = ''): void - { - static::assertThat($filename, new LogicalNot(new FileExists), $message); - } - - /** - * Asserts that a file exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileIsReadable(string $file, string $message = ''): void - { - self::assertFileExists($file, $message); - self::assertIsReadable($file, $message); - } - - /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotIsReadable(string $file, string $message = ''): void - { - self::assertFileExists($file, $message); - self::assertNotIsReadable($file, $message); - } - - /** - * Asserts that a file exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileIsWritable(string $file, string $message = ''): void - { - self::assertFileExists($file, $message); - self::assertIsWritable($file, $message); - } - - /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFileNotIsWritable(string $file, string $message = ''): void - { - self::assertFileExists($file, $message); - self::assertNotIsWritable($file, $message); - } - - /** - * Asserts that a condition is true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertTrue($condition, string $message = ''): void - { - static::assertThat($condition, static::isTrue(), $message); - } - - /** - * Asserts that a condition is not true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotTrue($condition, string $message = ''): void - { - static::assertThat($condition, static::logicalNot(static::isTrue()), $message); - } - - /** - * Asserts that a condition is false. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFalse($condition, string $message = ''): void - { - static::assertThat($condition, static::isFalse(), $message); - } - - /** - * Asserts that a condition is not false. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotFalse($condition, string $message = ''): void - { - static::assertThat($condition, static::logicalNot(static::isFalse()), $message); - } - - /** - * Asserts that a variable is null. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNull($actual, string $message = ''): void - { - static::assertThat($actual, static::isNull(), $message); - } - - /** - * Asserts that a variable is not null. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotNull($actual, string $message = ''): void - { - static::assertThat($actual, static::logicalNot(static::isNull()), $message); - } - - /** - * Asserts that a variable is finite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertFinite($actual, string $message = ''): void - { - static::assertThat($actual, static::isFinite(), $message); - } - - /** - * Asserts that a variable is infinite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertInfinite($actual, string $message = ''): void - { - static::assertThat($actual, static::isInfinite(), $message); - } - - /** - * Asserts that a variable is nan. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNan($actual, string $message = ''): void - { - static::assertThat($actual, static::isNan(), $message); - } - - /** - * Asserts that a class has a specified attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(1, 'valid attribute name'); - } - - if (!\class_exists($className)) { - throw InvalidArgumentHelper::factory(2, 'class name', $className); - } - - static::assertThat($className, new ClassHasAttribute($attributeName), $message); - } - - /** - * Asserts that a class does not have a specified attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(1, 'valid attribute name'); - } - - if (!\class_exists($className)) { - throw InvalidArgumentHelper::factory(2, 'class name', $className); - } - - static::assertThat( - $className, - new LogicalNot( - new ClassHasAttribute($attributeName) - ), - $message - ); - } - - /** - * Asserts that a class has a specified static attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(1, 'valid attribute name'); - } - - if (!\class_exists($className)) { - throw InvalidArgumentHelper::factory(2, 'class name', $className); - } - - static::assertThat( - $className, - new ClassHasStaticAttribute($attributeName), - $message - ); - } - - /** - * Asserts that a class does not have a specified static attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(1, 'valid attribute name'); - } - - if (!\class_exists($className)) { - throw InvalidArgumentHelper::factory(2, 'class name', $className); - } - - static::assertThat( - $className, - new LogicalNot( - new ClassHasStaticAttribute($attributeName) - ), - $message - ); - } - - /** - * Asserts that an object has a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(1, 'valid attribute name'); - } - - if (!\is_object($object)) { - throw InvalidArgumentHelper::factory(2, 'object'); - } - - static::assertThat( - $object, - new ObjectHasAttribute($attributeName), - $message - ); - } - - /** - * Asserts that an object does not have a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(1, 'valid attribute name'); - } - - if (!\is_object($object)) { - throw InvalidArgumentHelper::factory(2, 'object'); - } - - static::assertThat( - $object, - new LogicalNot( - new ObjectHasAttribute($attributeName) - ), - $message - ); - } - - /** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertSame($expected, $actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsIdentical($expected), - $message - ); - } - - /** - * Asserts that a variable and an attribute of an object have the same type - * and value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void - { - static::assertSame( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message - ); - } - - /** - * Asserts that two variables do not have the same type and value. - * Used on objects, it asserts that two variables do not reference - * the same object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotSame($expected, $actual, string $message = ''): void - { - if (\is_bool($expected) && \is_bool($actual)) { - static::assertNotEquals($expected, $actual, $message); - } - - static::assertThat( - $actual, - new LogicalNot( - new IsIdentical($expected) - ), - $message - ); - } - - /** - * Asserts that a variable and an attribute of an object do not have the - * same type and value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void - { - static::assertNotSame( - $expected, - static::readAttribute($actualClassOrObject, $actualAttributeName), - $message - ); - } - - /** - * Asserts that a variable is of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertInstanceOf(string $expected, $actual, string $message = ''): void - { - if (!\class_exists($expected) && !\interface_exists($expected)) { - throw InvalidArgumentHelper::factory(1, 'class or interface name'); - } - - static::assertThat( - $actual, - new IsInstanceOf($expected), - $message - ); - } - - /** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void - { - static::assertInstanceOf( - $expected, - static::readAttribute($classOrObject, $attributeName), - $message - ); - } - - /** - * Asserts that a variable is not of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void - { - if (!\class_exists($expected) && !\interface_exists($expected)) { - throw InvalidArgumentHelper::factory(1, 'class or interface name'); - } - - static::assertThat( - $actual, - new LogicalNot( - new IsInstanceOf($expected) - ), - $message - ); - } - - /** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void - { - static::assertNotInstanceOf( - $expected, - static::readAttribute($classOrObject, $attributeName), - $message - ); - } - - /** - * Asserts that a variable is of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369 - */ - public static function assertInternalType(string $expected, $actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType($expected), - $message - ); - } - - /** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void - { - static::assertInternalType( - $expected, - static::readAttribute($classOrObject, $attributeName), - $message - ); - } - - /** - * Asserts that a variable is of type array. - */ - public static function assertIsArray($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_ARRAY), - $message - ); - } - - /** - * Asserts that a variable is of type bool. - */ - public static function assertIsBool($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_BOOL), - $message - ); - } - - /** - * Asserts that a variable is of type float. - */ - public static function assertIsFloat($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_FLOAT), - $message - ); - } - - /** - * Asserts that a variable is of type int. - */ - public static function assertIsInt($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_INT), - $message - ); - } - - /** - * Asserts that a variable is of type numeric. - */ - public static function assertIsNumeric($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_NUMERIC), - $message - ); - } - - /** - * Asserts that a variable is of type object. - */ - public static function assertIsObject($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_OBJECT), - $message - ); - } - - /** - * Asserts that a variable is of type resource. - */ - public static function assertIsResource($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_RESOURCE), - $message - ); - } - - /** - * Asserts that a variable is of type string. - */ - public static function assertIsString($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_STRING), - $message - ); - } - - /** - * Asserts that a variable is of type scalar. - */ - public static function assertIsScalar($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_SCALAR), - $message - ); - } - - /** - * Asserts that a variable is of type callable. - */ - public static function assertIsCallable($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_CALLABLE), - $message - ); - } - - /** - * Asserts that a variable is of type iterable. - */ - public static function assertIsIterable($actual, string $message = ''): void - { - static::assertThat( - $actual, - new IsType(IsType::TYPE_ITERABLE), - $message - ); - } - - /** - * Asserts that a variable is not of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369 - */ - public static function assertNotInternalType(string $expected, $actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot( - new IsType($expected) - ), - $message - ); - } - - /** - * Asserts that a variable is not of type array. - */ - public static function assertIsNotArray($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_ARRAY)), - $message - ); - } - - /** - * Asserts that a variable is not of type bool. - */ - public static function assertIsNotBool($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_BOOL)), - $message - ); - } - - /** - * Asserts that a variable is not of type float. - */ - public static function assertIsNotFloat($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_FLOAT)), - $message - ); - } - - /** - * Asserts that a variable is not of type int. - */ - public static function assertIsNotInt($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_INT)), - $message - ); - } - - /** - * Asserts that a variable is not of type numeric. - */ - public static function assertIsNotNumeric($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_NUMERIC)), - $message - ); - } - - /** - * Asserts that a variable is not of type object. - */ - public static function assertIsNotObject($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_OBJECT)), - $message - ); - } - - /** - * Asserts that a variable is not of type resource. - */ - public static function assertIsNotResource($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_RESOURCE)), - $message - ); - } - - /** - * Asserts that a variable is not of type string. - */ - public static function assertIsNotString($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_STRING)), - $message - ); - } - - /** - * Asserts that a variable is not of type scalar. - */ - public static function assertIsNotScalar($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_SCALAR)), - $message - ); - } - - /** - * Asserts that a variable is not of type callable. - */ - public static function assertIsNotCallable($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_CALLABLE)), - $message - ); - } - - /** - * Asserts that a variable is not of type iterable. - */ - public static function assertIsNotIterable($actual, string $message = ''): void - { - static::assertThat( - $actual, - new LogicalNot(new IsType(IsType::TYPE_ITERABLE)), - $message - ); - } - - /** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void - { - static::assertNotInternalType( - $expected, - static::readAttribute($classOrObject, $attributeName), - $message - ); - } - - /** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertRegExp(string $pattern, string $string, string $message = ''): void - { - static::assertThat($string, new RegularExpression($pattern), $message); - } - - /** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotRegExp(string $pattern, string $string, string $message = ''): void - { - static::assertThat( - $string, - new LogicalNot( - new RegularExpression($pattern) - ), - $message - ); - } - - /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertSameSize($expected, $actual, string $message = ''): void - { - if (!$expected instanceof Countable && !\is_iterable($expected)) { - throw InvalidArgumentHelper::factory(1, 'countable or iterable'); - } - - if (!$actual instanceof Countable && !\is_iterable($actual)) { - throw InvalidArgumentHelper::factory(2, 'countable or iterable'); - } - - static::assertThat( - $actual, - new SameSize($expected), - $message - ); - } - - /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is not the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertNotSameSize($expected, $actual, string $message = ''): void - { - if (!$expected instanceof Countable && !\is_iterable($expected)) { - throw InvalidArgumentHelper::factory(1, 'countable or iterable'); - } - - if (!$actual instanceof Countable && !\is_iterable($actual)) { - throw InvalidArgumentHelper::factory(2, 'countable or iterable'); - } - - static::assertThat( - $actual, - new LogicalNot( - new SameSize($expected) - ), - $message - ); - } - - /** - * Asserts that a string matches a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void - { - static::assertThat($string, new StringMatchesFormatDescription($format), $message); - } - - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void - { - static::assertThat( - $string, - new LogicalNot( - new StringMatchesFormatDescription($format) - ), - $message - ); - } - - /** - * Asserts that a string matches a given format file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void - { - static::assertFileExists($formatFile, $message); - - static::assertThat( - $string, - new StringMatchesFormatDescription( - \file_get_contents($formatFile) - ), - $message - ); - } - - /** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void - { - static::assertFileExists($formatFile, $message); - - static::assertThat( - $string, - new LogicalNot( - new StringMatchesFormatDescription( - \file_get_contents($formatFile) - ) - ), - $message - ); - } - - /** - * Asserts that a string starts with a given prefix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void - { - static::assertThat($string, new StringStartsWith($prefix), $message); - } - - /** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringStartsNotWith($prefix, $string, string $message = ''): void - { - static::assertThat( - $string, - new LogicalNot( - new StringStartsWith($prefix) - ), - $message - ); - } - - public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void - { - $constraint = new StringContains($needle, false); - - static::assertThat($haystack, $constraint, $message); - } - - public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void - { - $constraint = new StringContains($needle, true); - - static::assertThat($haystack, $constraint, $message); - } - - public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void - { - $constraint = new LogicalNot(new StringContains($needle)); - - static::assertThat($haystack, $constraint, $message); - } - - public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void - { - $constraint = new LogicalNot(new StringContains($needle, true)); - - static::assertThat($haystack, $constraint, $message); - } - - /** - * Asserts that a string ends with a given suffix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void - { - static::assertThat($string, new StringEndsWith($suffix), $message); - } - - /** - * Asserts that a string ends not with a given suffix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void - { - static::assertThat( - $string, - new LogicalNot( - new StringEndsWith($suffix) - ), - $message - ); - } - - /** - * Asserts that two XML files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void - { - $expected = Xml::loadFile($expectedFile); - $actual = Xml::loadFile($actualFile); - - static::assertEquals($expected, $actual, $message); - } - - /** - * Asserts that two XML files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void - { - $expected = Xml::loadFile($expectedFile); - $actual = Xml::loadFile($actualFile); - - static::assertNotEquals($expected, $actual, $message); - } - - /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void - { - $expected = Xml::loadFile($expectedFile); - $actual = Xml::load($actualXml); - - static::assertEquals($expected, $actual, $message); - } - - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void - { - $expected = Xml::loadFile($expectedFile); - $actual = Xml::load($actualXml); - - static::assertNotEquals($expected, $actual, $message); - } - - /** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void - { - $expected = Xml::load($expectedXml); - $actual = Xml::load($actualXml); - - static::assertEquals($expected, $actual, $message); - } - - /** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void - { - $expected = Xml::load($expectedXml); - $actual = Xml::load($actualXml); - - static::assertNotEquals($expected, $actual, $message); - } - - /** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws AssertionFailedError - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void - { - $expectedElement = Xml::import($expectedElement); - $actualElement = Xml::import($actualElement); - - static::assertSame( - $expectedElement->tagName, - $actualElement->tagName, - $message - ); - - if ($checkAttributes) { - static::assertSame( - $expectedElement->attributes->length, - $actualElement->attributes->length, - \sprintf( - '%s%sNumber of attributes on node "%s" does not match', - $message, - !empty($message) ? "\n" : '', - $expectedElement->tagName - ) - ); - - for ($i = 0; $i < $expectedElement->attributes->length; $i++) { - /** @var \DOMAttr $expectedAttribute */ - $expectedAttribute = $expectedElement->attributes->item($i); - - /** @var \DOMAttr $actualAttribute */ - $actualAttribute = $actualElement->attributes->getNamedItem( - $expectedAttribute->name - ); - - if (!$actualAttribute) { - static::fail( - \sprintf( - '%s%sCould not find attribute "%s" on node "%s"', - $message, - !empty($message) ? "\n" : '', - $expectedAttribute->name, - $expectedElement->tagName - ) - ); - } - } - } - - Xml::removeCharacterDataNodes($expectedElement); - Xml::removeCharacterDataNodes($actualElement); - - static::assertSame( - $expectedElement->childNodes->length, - $actualElement->childNodes->length, - \sprintf( - '%s%sNumber of child nodes of "%s" differs', - $message, - !empty($message) ? "\n" : '', - $expectedElement->tagName - ) - ); - - for ($i = 0; $i < $expectedElement->childNodes->length; $i++) { - static::assertEqualXMLStructure( - $expectedElement->childNodes->item($i), - $actualElement->childNodes->item($i), - $checkAttributes, - $message - ); - } - } - - /** - * Evaluates a PHPUnit\Framework\Constraint matcher object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertThat($value, Constraint $constraint, string $message = ''): void - { - self::$count += \count($constraint); - - $constraint->evaluate($value, $message); - } - - /** - * Asserts that a string is a valid JSON string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJson(string $actualJson, string $message = ''): void - { - static::assertThat($actualJson, static::isJson(), $message); - } - - /** - * Asserts that two given JSON encoded objects or arrays are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void - { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - - static::assertThat($actualJson, new JsonMatches($expectedJson), $message); - } - - /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void - { - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - - static::assertThat( - $actualJson, - new LogicalNot( - new JsonMatches($expectedJson) - ), - $message - ); - } - - /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void - { - static::assertFileExists($expectedFile, $message); - $expectedJson = \file_get_contents($expectedFile); - - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - - static::assertThat($actualJson, new JsonMatches($expectedJson), $message); - } - - /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void - { - static::assertFileExists($expectedFile, $message); - $expectedJson = \file_get_contents($expectedFile); - - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - - static::assertThat( - $actualJson, - new LogicalNot( - new JsonMatches($expectedJson) - ), - $message - ); - } - - /** - * Asserts that two JSON files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void - { - static::assertFileExists($expectedFile, $message); - static::assertFileExists($actualFile, $message); - - $actualJson = \file_get_contents($actualFile); - $expectedJson = \file_get_contents($expectedFile); - - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - - $constraintExpected = new JsonMatches( - $expectedJson - ); - - $constraintActual = new JsonMatches($actualJson); - - static::assertThat($expectedJson, $constraintActual, $message); - static::assertThat($actualJson, $constraintExpected, $message); - } - - /** - * Asserts that two JSON files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void - { - static::assertFileExists($expectedFile, $message); - static::assertFileExists($actualFile, $message); - - $actualJson = \file_get_contents($actualFile); - $expectedJson = \file_get_contents($expectedFile); - - static::assertJson($expectedJson, $message); - static::assertJson($actualJson, $message); - - $constraintExpected = new JsonMatches( - $expectedJson - ); - - $constraintActual = new JsonMatches($actualJson); - - static::assertThat($expectedJson, new LogicalNot($constraintActual), $message); - static::assertThat($actualJson, new LogicalNot($constraintExpected), $message); - } - - public static function logicalAnd(): LogicalAnd - { - $constraints = \func_get_args(); - - $constraint = new LogicalAnd; - $constraint->setConstraints($constraints); - - return $constraint; - } - - public static function logicalOr(): LogicalOr - { - $constraints = \func_get_args(); - - $constraint = new LogicalOr; - $constraint->setConstraints($constraints); - - return $constraint; - } - - public static function logicalNot(Constraint $constraint): LogicalNot - { - return new LogicalNot($constraint); - } - - public static function logicalXor(): LogicalXor - { - $constraints = \func_get_args(); - - $constraint = new LogicalXor; - $constraint->setConstraints($constraints); - - return $constraint; - } - - public static function anything(): IsAnything - { - return new IsAnything; - } - - public static function isTrue(): IsTrue - { - return new IsTrue; - } - - public static function callback(callable $callback): Callback - { - return new Callback($callback); - } - - public static function isFalse(): IsFalse - { - return new IsFalse; - } - - public static function isJson(): IsJson - { - return new IsJson; - } - - public static function isNull(): IsNull - { - return new IsNull; - } - - public static function isFinite(): IsFinite - { - return new IsFinite; - } - - public static function isInfinite(): IsInfinite - { - return new IsInfinite; - } - - public static function isNan(): IsNan - { - return new IsNan; - } - - /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function attribute(Constraint $constraint, string $attributeName): Attribute - { - return new Attribute($constraint, $attributeName); - } - - public static function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains - { - return new TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity); - } - - public static function containsOnly(string $type): TraversableContainsOnly - { - return new TraversableContainsOnly($type); - } - - public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly - { - return new TraversableContainsOnly($className, false); - } - - /** - * @param int|string $key - */ - public static function arrayHasKey($key): ArrayHasKey - { - return new ArrayHasKey($key); - } - - public static function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual - { - return new IsEqual($value, $delta, $maxDepth, $canonicalize, $ignoreCase); - } - - /** - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute - { - return static::attribute( - static::equalTo( - $value, - $delta, - $maxDepth, - $canonicalize, - $ignoreCase - ), - $attributeName - ); - } - - public static function isEmpty(): IsEmpty - { - return new IsEmpty; - } - - public static function isWritable(): IsWritable - { - return new IsWritable; - } - - public static function isReadable(): IsReadable - { - return new IsReadable; - } - - public static function directoryExists(): DirectoryExists - { - return new DirectoryExists; - } - - public static function fileExists(): FileExists - { - return new FileExists; - } - - public static function greaterThan($value): GreaterThan - { - return new GreaterThan($value); - } - - public static function greaterThanOrEqual($value): LogicalOr - { - return static::logicalOr( - new IsEqual($value), - new GreaterThan($value) - ); - } - - public static function classHasAttribute(string $attributeName): ClassHasAttribute - { - return new ClassHasAttribute($attributeName); - } - - public static function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute - { - return new ClassHasStaticAttribute($attributeName); - } - - public static function objectHasAttribute($attributeName): ObjectHasAttribute - { - return new ObjectHasAttribute($attributeName); - } - - public static function identicalTo($value): IsIdentical - { - return new IsIdentical($value); - } - - public static function isInstanceOf(string $className): IsInstanceOf - { - return new IsInstanceOf($className); - } - - public static function isType(string $type): IsType - { - return new IsType($type); - } - - public static function lessThan($value): LessThan - { - return new LessThan($value); - } - - public static function lessThanOrEqual($value): LogicalOr - { - return static::logicalOr( - new IsEqual($value), - new LessThan($value) - ); - } - - public static function matchesRegularExpression(string $pattern): RegularExpression - { - return new RegularExpression($pattern); - } - - public static function matches(string $string): StringMatchesFormatDescription - { - return new StringMatchesFormatDescription($string); - } - - public static function stringStartsWith($prefix): StringStartsWith - { - return new StringStartsWith($prefix); - } - - public static function stringContains(string $string, bool $case = true): StringContains - { - return new StringContains($string, $case); - } - - public static function stringEndsWith(string $suffix): StringEndsWith - { - return new StringEndsWith($suffix); - } - - public static function countOf(int $count): Count - { - return new Count($count); - } - - /** - * Fails a test with the given message. - * - * @throws AssertionFailedError - */ - public static function fail(string $message = ''): void - { - self::$count++; - - throw new AssertionFailedError($message); - } - - /** - * Returns the value of an attribute of a class or an object. - * This also works for attributes that are declared protected or private. - * - * @param object|string $classOrObject - * - * @throws Exception - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function readAttribute($classOrObject, string $attributeName) - { - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(2, 'valid attribute name'); - } - - if (\is_string($classOrObject)) { - if (!\class_exists($classOrObject)) { - throw InvalidArgumentHelper::factory( - 1, - 'class name' - ); - } - - return static::getStaticAttribute( - $classOrObject, - $attributeName - ); - } - - if (\is_object($classOrObject)) { - return static::getObjectAttribute( - $classOrObject, - $attributeName - ); - } - - throw InvalidArgumentHelper::factory( - 1, - 'class name or object' - ); - } - - /** - * Returns the value of a static attribute. - * This also works for attributes that are declared protected or private. - * - * @throws Exception - * @throws ReflectionException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function getStaticAttribute(string $className, string $attributeName) - { - if (!\class_exists($className)) { - throw InvalidArgumentHelper::factory(1, 'class name'); - } - - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(2, 'valid attribute name'); - } - - $class = new ReflectionClass($className); - - while ($class) { - $attributes = $class->getStaticProperties(); - - if (\array_key_exists($attributeName, $attributes)) { - return $attributes[$attributeName]; - } - - $class = $class->getParentClass(); - } - - throw new Exception( - \sprintf( - 'Attribute "%s" not found in class.', - $attributeName - ) - ); - } - - /** - * Returns the value of an object's attribute. - * This also works for attributes that are declared protected or private. - * - * @param object $object - * - * @throws Exception - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338 - */ - public static function getObjectAttribute($object, string $attributeName) - { - if (!\is_object($object)) { - throw InvalidArgumentHelper::factory(1, 'object'); - } - - if (!self::isValidAttributeName($attributeName)) { - throw InvalidArgumentHelper::factory(2, 'valid attribute name'); - } - - try { - $reflector = new ReflectionObject($object); - - do { - try { - $attribute = $reflector->getProperty($attributeName); - - if (!$attribute || $attribute->isPublic()) { - return $object->$attributeName; - } - - $attribute->setAccessible(true); - $value = $attribute->getValue($object); - $attribute->setAccessible(false); - - return $value; - } catch (ReflectionException $e) { - } - } while ($reflector = $reflector->getParentClass()); - } catch (ReflectionException $e) { - } - - throw new Exception( - \sprintf( - 'Attribute "%s" not found in object.', - $attributeName - ) - ); - } - - /** - * Mark the test as incomplete. - * - * @throws IncompleteTestError - */ - public static function markTestIncomplete(string $message = ''): void - { - throw new IncompleteTestError($message); - } - - /** - * Mark the test as skipped. - * - * @throws SkippedTestError - */ - public static function markTestSkipped(string $message = ''): void - { - throw new SkippedTestError($message); - } - - /** - * Return the current assertion count. - */ - public static function getCount(): int - { - return self::$count; - } - - /** - * Reset the assertion counter. - */ - public static function resetCount(): void - { - self::$count = 0; - } - - private static function isValidAttributeName(string $attributeName): bool - { - return \preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName); - } - - private static function createWarning(string $warning): void - { - foreach (\debug_backtrace() as $step) { - if (isset($step['object']) && $step['object'] instanceof TestCase) { - $step['object']->addWarning($warning); - - break; - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use PHPUnit\Util\Filter; - -/** - * Base class for all PHPUnit Framework exceptions. - * - * Ensures that exceptions thrown during a test run do not leave stray - * references behind. - * - * Every Exception contains a stack trace. Each stack frame contains the 'args' - * of the called function. The function arguments can contain references to - * instantiated objects. The references prevent the objects from being - * destructed (until test results are eventually printed), so memory cannot be - * freed up. - * - * With enabled process isolation, test results are serialized in the child - * process and unserialized in the parent process. The stack trace of Exceptions - * may contain objects that cannot be serialized or unserialized (e.g., PDO - * connections). Unserializing user-space objects from the child process into - * the parent would break the intended encapsulation of process isolation. - * - * @see http://fabien.potencier.org/article/9/php-serialization-stack-traces-and-exceptions - */ -class Exception extends \RuntimeException implements \PHPUnit\Exception -{ - /** - * @var array - */ - protected $serializableTrace; - - public function __construct($message = '', $code = 0, \Exception $previous = null) - { - parent::__construct($message, $code, $previous); - - $this->serializableTrace = $this->getTrace(); - - foreach ($this->serializableTrace as $i => $call) { - unset($this->serializableTrace[$i]['args']); - } - } - - /** - * @throws \InvalidArgumentException - */ - public function __toString(): string - { - $string = TestFailure::exceptionToString($this); - - if ($trace = Filter::getFilteredStacktrace($this)) { - $string .= "\n" . $trace; - } - - return $string; - } - - public function __sleep(): array - { - return \array_keys(\get_object_vars($this)); - } - - /** - * Returns the serializable trace (without 'args'). - */ - public function getSerializableTrace(): array - { - return $this->serializableTrace; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * A skipped test case - */ -class SkippedTestCase extends TestCase -{ - /** - * @var string - */ - protected $message = ''; - - /** - * @var bool - */ - protected $backupGlobals = false; - - /** - * @var bool - */ - protected $backupStaticAttributes = false; - - /** - * @var bool - */ - protected $runTestInSeparateProcess = false; - - /** - * @var bool - */ - protected $useErrorHandler = false; - - /** - * @var bool - */ - protected $useOutputBuffering = false; - - public function __construct(string $className, string $methodName, string $message = '') - { - parent::__construct($className . '::' . $methodName); - - $this->message = $message; - } - - public function getMessage(): string - { - return $this->message; - } - - /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - return $this->getName(); - } - - /** - * @throws Exception - */ - protected function runTest(): void - { - $this->markTestSkipped($this->message); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use Countable; - -/** - * A Test can be run and collect its results. - */ -interface Test extends Countable -{ - /** - * Runs a test and collects its result in a TestResult instance. - */ - public function run(TestResult $result = null): TestResult; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Util\Json; -use SebastianBergmann\Comparator\ComparisonFailure; - -/** - * Asserts whether or not two JSON objects are equal. - */ -class JsonMatches extends Constraint -{ - /** - * @var string - */ - private $value; - - public function __construct(string $value) - { - parent::__construct(); - - $this->value = $value; - } - - /** - * Returns a string representation of the object. - */ - public function toString(): string - { - return \sprintf( - 'matches JSON string "%s"', - $this->value - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * This method can be overridden to implement the evaluation algorithm. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - [$error, $recodedOther] = Json::canonicalize($other); - - if ($error) { - return false; - } - - [$error, $recodedValue] = Json::canonicalize($this->value); - - if ($error) { - return false; - } - - return $recodedOther == $recodedValue; - } - - /** - * Throws an exception for the given compared value and test description - * - * @param mixed $other evaluated value or object - * @param string $description Additional information about the test - * @param ComparisonFailure $comparisonFailure - * - * @throws ExpectationFailedException - * @throws \PHPUnit\Framework\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void - { - if ($comparisonFailure === null) { - [$error] = Json::canonicalize($other); - - if ($error) { - parent::fail($other, $description); - - return; - } - - [$error] = Json::canonicalize($this->value); - - if ($error) { - parent::fail($other, $description); - - return; - } - - $comparisonFailure = new ComparisonFailure( - \json_decode($this->value), - \json_decode($other), - Json::prettify($this->value), - Json::prettify($other), - false, - 'Failed asserting that two json values are equal.' - ); - } - - parent::fail($other, $description, $comparisonFailure); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the value it is evaluated for is greater - * than a given value. - */ -class GreaterThan extends Constraint -{ - /** - * @var float|int - */ - private $value; - - /** - * @param float|int $value - */ - public function __construct($value) - { - parent::__construct(); - - $this->value = $value; - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - return 'is greater than ' . $this->exporter->export($this->value); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $this->value < $other; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that accepts finite. - */ -class IsFinite extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is finite'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \is_finite($other); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that checks if the file/dir(name) that it is evaluated for is readable. - * - * The file path to check is passed as $other in evaluate(). - */ -class IsReadable extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is readable'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \is_readable($other); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - '"%s" is readable', - $other - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Logical XOR. - */ -class LogicalXor extends Constraint -{ - /** - * @var Constraint[] - */ - private $constraints = []; - - public static function fromConstraints(Constraint ...$constraints): self - { - $constraint = new self; - - $constraint->constraints = \array_values($constraints); - - return $constraint; - } - - /** - * @param Constraint[] $constraints - */ - public function setConstraints(array $constraints): void - { - $this->constraints = []; - - foreach ($constraints as $constraint) { - if (!($constraint instanceof Constraint)) { - $constraint = new IsEqual( - $constraint - ); - } - - $this->constraints[] = $constraint; - } - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - $success = true; - $lastResult = null; - - foreach ($this->constraints as $constraint) { - $result = $constraint->evaluate($other, $description, true); - - if ($result === $lastResult) { - $success = false; - - break; - } - - $lastResult = $result; - } - - if ($returnResult) { - return $success; - } - - if (!$success) { - $this->fail($other, $description); - } - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - $text = ''; - - foreach ($this->constraints as $key => $constraint) { - if ($key > 0) { - $text .= ' xor '; - } - - $text .= $constraint->toString(); - } - - return $text; - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - $count = 0; - - foreach ($this->constraints as $constraint) { - $count += \count($constraint); - } - - return $count; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use SebastianBergmann\Diff\Differ; - -/** - * ... - */ -class StringMatchesFormatDescription extends RegularExpression -{ - /** - * @var string - */ - private $string; - - public function __construct(string $string) - { - parent::__construct( - $this->createPatternFromFormat( - $this->convertNewlines($string) - ) - ); - - $this->string = $string; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return parent::matches( - $this->convertNewlines($other) - ); - } - - protected function failureDescription($other): string - { - return 'string matches format description'; - } - - protected function additionalFailureDescription($other): string - { - $from = \explode("\n", $this->string); - $to = \explode("\n", $this->convertNewlines($other)); - - foreach ($from as $index => $line) { - if (isset($to[$index]) && $line !== $to[$index]) { - $line = $this->createPatternFromFormat($line); - - if (\preg_match($line, $to[$index]) > 0) { - $from[$index] = $to[$index]; - } - } - } - - $this->string = \implode("\n", $from); - $other = \implode("\n", $to); - - $differ = new Differ("--- Expected\n+++ Actual\n"); - - return $differ->diff($this->string, $other); - } - - private function createPatternFromFormat(string $string): string - { - $string = \strtr( - \preg_quote($string, '/'), - [ - '%%' => '%', - '%e' => '\\' . \DIRECTORY_SEPARATOR, - '%s' => '[^\r\n]+', - '%S' => '[^\r\n]*', - '%a' => '.+', - '%A' => '.*', - '%w' => '\s*', - '%i' => '[+-]?\d+', - '%d' => '\d+', - '%x' => '[0-9a-fA-F]+', - '%f' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', - '%c' => '.', - ] - ); - - return '/^' . $string . '$/s'; - } - - private function convertNewlines($text): string - { - return \preg_replace('/\r\n/', "\n", $text); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that accepts infinite. - */ -class IsInfinite extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is infinite'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \is_infinite($other); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the value it is evaluated for is of a - * specified type. - * - * The expected value is passed in the constructor. - */ -class IsType extends Constraint -{ - public const TYPE_ARRAY = 'array'; - - public const TYPE_BOOL = 'bool'; - - public const TYPE_FLOAT = 'float'; - - public const TYPE_INT = 'int'; - - public const TYPE_NULL = 'null'; - - public const TYPE_NUMERIC = 'numeric'; - - public const TYPE_OBJECT = 'object'; - - public const TYPE_RESOURCE = 'resource'; - - public const TYPE_STRING = 'string'; - - public const TYPE_SCALAR = 'scalar'; - - public const TYPE_CALLABLE = 'callable'; - - public const TYPE_ITERABLE = 'iterable'; - - /** - * @var array - */ - private const KNOWN_TYPES = [ - 'array' => true, - 'boolean' => true, - 'bool' => true, - 'double' => true, - 'float' => true, - 'integer' => true, - 'int' => true, - 'null' => true, - 'numeric' => true, - 'object' => true, - 'real' => true, - 'resource' => true, - 'string' => true, - 'scalar' => true, - 'callable' => true, - 'iterable' => true, - ]; - - /** - * @var string - */ - private $type; - - /** - * @throws \PHPUnit\Framework\Exception - */ - public function __construct(string $type) - { - parent::__construct(); - - if (!isset(self::KNOWN_TYPES[$type])) { - throw new \PHPUnit\Framework\Exception( - \sprintf( - 'Type specified for PHPUnit\Framework\Constraint\IsType <%s> ' . - 'is not a valid type.', - $type - ) - ); - } - - $this->type = $type; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return \sprintf( - 'is of type "%s"', - $this->type - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - switch ($this->type) { - case 'numeric': - return \is_numeric($other); - - case 'integer': - case 'int': - return \is_int($other); - - case 'double': - case 'float': - case 'real': - return \is_float($other); - - case 'string': - return \is_string($other); - - case 'boolean': - case 'bool': - return \is_bool($other); - - case 'null': - return null === $other; - - case 'array': - return \is_array($other); - - case 'object': - return \is_object($other); - - case 'resource': - return \is_resource($other) || \is_string(@\get_resource_type($other)); - - case 'scalar': - return \is_scalar($other); - - case 'callable': - return \is_callable($other); - - case 'iterable': - return \is_iterable($other); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use ArrayAccess; - -/** - * Constraint that asserts that the array it is evaluated for has a given key. - * - * Uses array_key_exists() to check if the key is found in the input array, if - * not found the evaluation fails. - * - * The array key is passed in the constructor. - */ -class ArrayHasKey extends Constraint -{ - /** - * @var int|string - */ - private $key; - - /** - * @param int|string $key - */ - public function __construct($key) - { - parent::__construct(); - $this->key = $key; - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - return 'has the key ' . $this->exporter->export($this->key); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - if (\is_array($other)) { - return \array_key_exists($this->key, $other); - } - - if ($other instanceof ArrayAccess) { - return $other->offsetExists($this->key); - } - - return false; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - return 'an array ' . $this->toString(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -class ExceptionCode extends Constraint -{ - /** - * @var int|string - */ - private $expectedCode; - - /** - * @param int|string $expected - */ - public function __construct($expected) - { - parent::__construct(); - - $this->expectedCode = $expected; - } - - public function toString(): string - { - return 'exception code is '; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param \Throwable $other - */ - protected function matches($other): bool - { - return (string) $other->getCode() === (string) $this->expectedCode; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - return \sprintf( - '%s is equal to expected exception code %s', - $this->exporter->export($other->getCode()), - $this->exporter->export($this->expectedCode) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use ReflectionClass; -use ReflectionException; - -/** - * Constraint that asserts that the object it is evaluated for is an instance - * of a given class. - * - * The expected class name is passed in the constructor. - */ -class IsInstanceOf extends Constraint -{ - /** - * @var string - */ - private $className; - - public function __construct(string $className) - { - parent::__construct(); - - $this->className = $className; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return \sprintf( - 'is instance of %s "%s"', - $this->getType(), - $this->className - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $other instanceof $this->className; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - return \sprintf( - '%s is an instance of %s "%s"', - $this->exporter->shortenedExport($other), - $this->getType(), - $this->className - ); - } - - private function getType(): string - { - try { - $reflection = new ReflectionClass($this->className); - - if ($reflection->isInterface()) { - return 'interface'; - } - } catch (ReflectionException $e) { - } - - return 'class'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -class SameSize extends Count -{ - public function __construct(iterable $expected) - { - parent::__construct($this->getCountOf($expected)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Constraint that asserts that the Traversable it is applied to contains - * only values of a given type. - */ -class TraversableContainsOnly extends Constraint -{ - /** - * @var Constraint - */ - private $constraint; - - /** - * @var string - */ - private $type; - - /** - * @throws \PHPUnit\Framework\Exception - */ - public function __construct(string $type, bool $isNativeType = true) - { - parent::__construct(); - - if ($isNativeType) { - $this->constraint = new IsType($type); - } else { - $this->constraint = new IsInstanceOf( - $type - ); - } - - $this->type = $type; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - $success = true; - - foreach ($other as $item) { - if (!$this->constraint->evaluate($item, '', true)) { - $success = false; - - break; - } - } - - if ($returnResult) { - return $success; - } - - if (!$success) { - $this->fail($other, $description); - } - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'contains only values of type "' . $this->type . '"'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that evaluates against a specified closure. - */ -class Callback extends Constraint -{ - /** - * @var callable - */ - private $callback; - - public function __construct(callable $callback) - { - parent::__construct(); - - $this->callback = $callback; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is accepted by specified callback'; - } - - /** - * Evaluates the constraint for parameter $value. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \call_user_func($this->callback, $other); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -class ExceptionMessage extends Constraint -{ - /** - * @var string - */ - private $expectedMessage; - - public function __construct(string $expected) - { - parent::__construct(); - - $this->expectedMessage = $expected; - } - - public function toString(): string - { - if ($this->expectedMessage === '') { - return 'exception message is empty'; - } - - return 'exception message contains '; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param \Throwable $other - */ - protected function matches($other): bool - { - if ($this->expectedMessage === '') { - return $other->getMessage() === ''; - } - - return \strpos($other->getMessage(), $this->expectedMessage) !== false; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - if ($this->expectedMessage === '') { - return \sprintf( - "exception message is empty but is '%s'", - $other->getMessage() - ); - } - - return \sprintf( - "exception message '%s' contains '%s'", - $other->getMessage(), - $this->expectedMessage - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that checks if the directory(name) that it is evaluated for exists. - * - * The file path to check is passed as $other in evaluate(). - */ -class DirectoryExists extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'directory exists'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \is_dir($other); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - 'directory "%s" exists', - $other - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the value it is evaluated for is less than - * a given value. - */ -class LessThan extends Constraint -{ - /** - * @var float|int - */ - private $value; - - /** - * @param float|int $value - */ - public function __construct($value) - { - parent::__construct(); - - $this->value = $value; - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - return 'is less than ' . $this->exporter->export($this->value); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $this->value > $other; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use ReflectionObject; - -/** - * Constraint that asserts that the object it is evaluated for has a given - * attribute. - * - * The attribute name is passed in the constructor. - */ -class ObjectHasAttribute extends ClassHasAttribute -{ - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - $object = new ReflectionObject($other); - - return $object->hasProperty($this->attributeName()); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the string it is evaluated for matches - * a regular expression. - * - * Checks a given value using the Perl Compatible Regular Expression extension - * in PHP. The pattern is matched by executing preg_match(). - * - * The pattern string passed in the constructor. - */ -class RegularExpression extends Constraint -{ - /** - * @var string - */ - private $pattern; - - public function __construct(string $pattern) - { - parent::__construct(); - - $this->pattern = $pattern; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return \sprintf( - 'matches PCRE pattern "%s"', - $this->pattern - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \preg_match($this->pattern, $other) > 0; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Util\Filter; -use Throwable; - -class Exception extends Constraint -{ - /** - * @var string - */ - private $className; - - public function __construct(string $className) - { - parent::__construct(); - - $this->className = $className; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return \sprintf( - 'exception of type "%s"', - $this->className - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $other instanceof $this->className; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - if ($other !== null) { - $message = ''; - - if ($other instanceof Throwable) { - $message = '. Message was: "' . $other->getMessage() . '" at' - . "\n" . Filter::getFilteredStacktrace($other); - } - - return \sprintf( - 'exception of type "%s" matches expected exception "%s"%s', - \get_class($other), - $this->className, - $message - ); - } - - return \sprintf( - 'exception of type "%s" is thrown', - $this->className - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use SplObjectStorage; - -/** - * Constraint that asserts that the Traversable it is applied to contains - * a given value. - */ -class TraversableContains extends Constraint -{ - /** - * @var bool - */ - private $checkForObjectIdentity; - - /** - * @var bool - */ - private $checkForNonObjectIdentity; - - /** - * @var mixed - */ - private $value; - - /** - * @throws \PHPUnit\Framework\Exception - */ - public function __construct($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false) - { - parent::__construct(); - - $this->checkForObjectIdentity = $checkForObjectIdentity; - $this->checkForNonObjectIdentity = $checkForNonObjectIdentity; - $this->value = $value; - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - if (\is_string($this->value) && \strpos($this->value, "\n") !== false) { - return 'contains "' . $this->value . '"'; - } - - return 'contains ' . $this->exporter->export($this->value); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - if ($other instanceof SplObjectStorage) { - return $other->contains($this->value); - } - - if (\is_object($this->value)) { - foreach ($other as $element) { - if ($this->checkForObjectIdentity && $element === $this->value) { - return true; - } - - if (!$this->checkForObjectIdentity && $element == $this->value) { - return true; - } - } - } else { - foreach ($other as $element) { - if ($this->checkForNonObjectIdentity && $element === $this->value) { - return true; - } - - if (!$this->checkForNonObjectIdentity && $element == $this->value) { - return true; - } - } - } - - return false; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - return \sprintf( - '%s %s', - \is_array($other) ? 'an array' : 'a traversable', - $this->toString() - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the string it is evaluated for ends with a given - * suffix. - */ -class StringEndsWith extends Constraint -{ - /** - * @var string - */ - private $suffix; - - public function __construct(string $suffix) - { - parent::__construct(); - - $this->suffix = $suffix; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'ends with "' . $this->suffix . '"'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \substr($other, 0 - \strlen($this->suffix)) === $this->suffix; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; -use SebastianBergmann\Comparator\ComparisonFailure; -use SebastianBergmann\Comparator\Factory as ComparatorFactory; - -/** - * Constraint that checks if one value is equal to another. - * - * Equality is checked with PHP's == operator, the operator is explained in - * detail at {@url https://php.net/manual/en/types.comparisons.php}. - * Two values are equal if they have the same value disregarding type. - * - * The expected value is passed in the constructor. - */ -class IsEqual extends Constraint -{ - /** - * @var mixed - */ - private $value; - - /** - * @var float - */ - private $delta; - - /** - * @var int - */ - private $maxDepth; - - /** - * @var bool - */ - private $canonicalize; - - /** - * @var bool - */ - private $ignoreCase; - - public function __construct($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false) - { - parent::__construct(); - - $this->value = $value; - $this->delta = $delta; - $this->maxDepth = $maxDepth; - $this->canonicalize = $canonicalize; - $this->ignoreCase = $ignoreCase; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - // If $this->value and $other are identical, they are also equal. - // This is the most common path and will allow us to skip - // initialization of all the comparators. - if ($this->value === $other) { - return true; - } - - $comparatorFactory = ComparatorFactory::getInstance(); - - try { - $comparator = $comparatorFactory->getComparatorFor( - $this->value, - $other - ); - - $comparator->assertEquals( - $this->value, - $other, - $this->delta, - $this->canonicalize, - $this->ignoreCase - ); - } catch (ComparisonFailure $f) { - if ($returnResult) { - return false; - } - - throw new ExpectationFailedException( - \trim($description . "\n" . $f->getMessage()), - $f - ); - } - - return true; - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - $delta = ''; - - if (\is_string($this->value)) { - if (\strpos($this->value, "\n") !== false) { - return 'is equal to '; - } - - return \sprintf( - "is equal to '%s'", - $this->value - ); - } - - if ($this->delta != 0) { - $delta = \sprintf( - ' with delta <%F>', - $this->delta - ); - } - - return \sprintf( - 'is equal to %s%s', - $this->exporter->export($this->value), - $delta - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; -use SebastianBergmann\Comparator\ComparisonFailure; - -/** - * Constraint that asserts that the array it is evaluated for has a specified subset. - * - * Uses array_replace_recursive() to check if a key value subset is part of the - * subject array. - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494 - */ -class ArraySubset extends Constraint -{ - /** - * @var iterable - */ - private $subset; - - /** - * @var bool - */ - private $strict; - - public function __construct(iterable $subset, bool $strict = false) - { - parent::__construct(); - - $this->strict = $strict; - $this->subset = $subset; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - //type cast $other & $this->subset as an array to allow - //support in standard array functions. - $other = $this->toArray($other); - $this->subset = $this->toArray($this->subset); - - $patched = \array_replace_recursive($other, $this->subset); - - if ($this->strict) { - $result = $other === $patched; - } else { - $result = $other == $patched; - } - - if ($returnResult) { - return $result; - } - - if (!$result) { - $f = new ComparisonFailure( - $patched, - $other, - \var_export($patched, true), - \var_export($other, true) - ); - - $this->fail($other, $description, $f); - } - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - return 'has the subset ' . $this->exporter->export($this->subset); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - return 'an array ' . $this->toString(); - } - - private function toArray(iterable $other): array - { - if (\is_array($other)) { - return $other; - } - - if ($other instanceof \ArrayObject) { - return $other->getArrayCopy(); - } - - if ($other instanceof \Traversable) { - return \iterator_to_array($other); - } - - // Keep BC even if we know that array would not be the expected one - return (array) $other; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\ExpectationFailedException; - -class Attribute extends Composite -{ - /** - * @var string - */ - private $attributeName; - - public function __construct(Constraint $constraint, string $attributeName) - { - parent::__construct($constraint); - - $this->attributeName = $attributeName; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \PHPUnit\Framework\Exception - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - return parent::evaluate( - Assert::readAttribute( - $other, - $this->attributeName - ), - $description, - $returnResult - ); - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'attribute "' . $this->attributeName . '" ' . $this->innerConstraint()->toString(); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return $this->toString(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that checks if the file/dir(name) that it is evaluated for is writable. - * - * The file path to check is passed as $other in evaluate(). - */ -class IsWritable extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is writable'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \is_writable($other); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - '"%s" is writable', - $other - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use Countable; - -/** - * Constraint that checks whether a variable is empty(). - */ -class IsEmpty extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is empty'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - if ($other instanceof Countable) { - return \count($other) === 0; - } - - return empty($other); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - $type = \gettype($other); - - return \sprintf( - '%s %s %s', - $type[0] == 'a' || $type[0] == 'o' ? 'an' : 'a', - $type, - $this->toString() - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the string it is evaluated for begins with a - * given prefix. - */ -class StringStartsWith extends Constraint -{ - /** - * @var string - */ - private $prefix; - - public function __construct(string $prefix) - { - parent::__construct(); - - $this->prefix = $prefix; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'starts with "' . $this->prefix . '"'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \strpos($other, $this->prefix) === 0; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that accepts false. - */ -class IsFalse extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is false'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $other === false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -abstract class Composite extends Constraint -{ - /** - * @var Constraint - */ - private $innerConstraint; - - public function __construct(Constraint $innerConstraint) - { - parent::__construct(); - - $this->innerConstraint = $innerConstraint; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - try { - return $this->innerConstraint->evaluate( - $other, - $description, - $returnResult - ); - } catch (ExpectationFailedException $e) { - $this->fail($other, $description, $e->getComparisonFailure()); - } - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - return \count($this->innerConstraint); - } - - protected function innerConstraint(): Constraint - { - return $this->innerConstraint; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use Countable; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\SelfDescribing; -use SebastianBergmann\Comparator\ComparisonFailure; -use SebastianBergmann\Exporter\Exporter; - -/** - * Abstract base class for constraints which can be applied to any value. - */ -abstract class Constraint implements Countable, SelfDescribing -{ - protected $exporter; - - public function __construct() - { - $this->exporter = new Exporter; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - $success = false; - - if ($this->matches($other)) { - $success = true; - } - - if ($returnResult) { - return $success; - } - - if (!$success) { - $this->fail($other, $description); - } - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - return 1; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * This method can be overridden to implement the evaluation algorithm. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return false; - } - - /** - * Throws an exception for the given compared value and test description - * - * @param mixed $other evaluated value or object - * @param string $description Additional information about the test - * @param ComparisonFailure $comparisonFailure - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function fail($other, $description, ComparisonFailure $comparisonFailure = null): void - { - $failureDescription = \sprintf( - 'Failed asserting that %s.', - $this->failureDescription($other) - ); - - $additionalFailureDescription = $this->additionalFailureDescription($other); - - if ($additionalFailureDescription) { - $failureDescription .= "\n" . $additionalFailureDescription; - } - - if (!empty($description)) { - $failureDescription = $description . "\n" . $failureDescription; - } - - throw new ExpectationFailedException( - $failureDescription, - $comparisonFailure - ); - } - - /** - * Return additional failure description where needed - * - * The function can be overridden to provide additional failure - * information like a diff - * - * @param mixed $other evaluated value or object - */ - protected function additionalFailureDescription($other): string - { - return ''; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * To provide additional failure information additionalFailureDescription - * can be used. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - return $this->exporter->export($other) . ' ' . $this->toString(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Logical OR. - */ -class LogicalOr extends Constraint -{ - /** - * @var Constraint[] - */ - private $constraints = []; - - public static function fromConstraints(Constraint ...$constraints): self - { - $constraint = new self; - - $constraint->constraints = \array_values($constraints); - - return $constraint; - } - - /** - * @param Constraint[] $constraints - */ - public function setConstraints(array $constraints): void - { - $this->constraints = []; - - foreach ($constraints as $constraint) { - if (!($constraint instanceof Constraint)) { - $constraint = new IsEqual( - $constraint - ); - } - - $this->constraints[] = $constraint; - } - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - $success = false; - - foreach ($this->constraints as $constraint) { - if ($constraint->evaluate($other, $description, true)) { - $success = true; - - break; - } - } - - if ($returnResult) { - return $success; - } - - if (!$success) { - $this->fail($other, $description); - } - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - $text = ''; - - foreach ($this->constraints as $key => $constraint) { - if ($key > 0) { - $text .= ' or '; - } - - $text .= $constraint->toString(); - } - - return $text; - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - $count = 0; - - foreach ($this->constraints as $constraint) { - $count += \count($constraint); - } - - return $count; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Util\RegularExpression as RegularExpressionUtil; - -class ExceptionMessageRegularExpression extends Constraint -{ - /** - * @var string - */ - private $expectedMessageRegExp; - - public function __construct(string $expected) - { - parent::__construct(); - - $this->expectedMessageRegExp = $expected; - } - - public function toString(): string - { - return 'exception message matches '; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param \PHPUnit\Framework\Exception $other - * - * @throws \Exception - * @throws \PHPUnit\Framework\Exception - */ - protected function matches($other): bool - { - $match = RegularExpressionUtil::safeMatch($this->expectedMessageRegExp, $other->getMessage()); - - if ($match === false) { - throw new \PHPUnit\Framework\Exception( - "Invalid expected exception message regex given: '{$this->expectedMessageRegExp}'" - ); - } - - return $match === 1; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - "exception message '%s' matches '%s'", - $other->getMessage(), - $this->expectedMessageRegExp - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that accepts nan. - */ -class IsNan extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is nan'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \is_nan($other); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; -use SebastianBergmann\Comparator\ComparisonFailure; - -/** - * Constraint that asserts that one value is identical to another. - * - * Identical check is performed with PHP's === operator, the operator is - * explained in detail at - * {@url https://php.net/manual/en/types.comparisons.php}. - * Two values are identical if they have the same value and are of the same - * type. - * - * The expected value is passed in the constructor. - */ -class IsIdentical extends Constraint -{ - /** - * @var float - */ - private const EPSILON = 0.0000000001; - - /** - * @var mixed - */ - private $value; - - public function __construct($value) - { - parent::__construct(); - - $this->value = $value; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - if (\is_float($this->value) && \is_float($other) && - !\is_infinite($this->value) && !\is_infinite($other) && - !\is_nan($this->value) && !\is_nan($other)) { - $success = \abs($this->value - $other) < self::EPSILON; - } else { - $success = $this->value === $other; - } - - if ($returnResult) { - return $success; - } - - if (!$success) { - $f = null; - - // if both values are strings, make sure a diff is generated - if (\is_string($this->value) && \is_string($other)) { - $f = new ComparisonFailure( - $this->value, - $other, - \sprintf("'%s'", $this->value), - \sprintf("'%s'", $other) - ); - } - - // if both values are array, make sure a diff is generated - if (\is_array($this->value) && \is_array($other)) { - $f = new ComparisonFailure( - $this->value, - $other, - $this->exporter->export($this->value), - $this->exporter->export($other) - ); - } - - $this->fail($other, $description, $f); - } - } - - /** - * Returns a string representation of the constraint. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - if (\is_object($this->value)) { - return 'is identical to an object of class "' . - \get_class($this->value) . '"'; - } - - return 'is identical to ' . $this->exporter->export($this->value); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - if (\is_object($this->value) && \is_object($other)) { - return 'two variables reference the same object'; - } - - if (\is_string($this->value) && \is_string($other)) { - return 'two strings are identical'; - } - - if (\is_array($this->value) && \is_array($other)) { - return 'two arrays are identical'; - } - - return parent::failureDescription($other); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use ReflectionClass; - -/** - * Constraint that asserts that the class it is evaluated for has a given - * static attribute. - * - * The attribute name is passed in the constructor. - */ -class ClassHasStaticAttribute extends ClassHasAttribute -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return \sprintf( - 'has static attribute "%s"', - $this->attributeName() - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - $class = new ReflectionClass($other); - - if ($class->hasProperty($this->attributeName())) { - $attribute = $class->getProperty($this->attributeName()); - - return $attribute->isStatic(); - } - - return false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use Countable; -use Generator; -use Iterator; -use IteratorAggregate; -use Traversable; - -class Count extends Constraint -{ - /** - * @var int - */ - private $expectedCount; - - public function __construct(int $expected) - { - parent::__construct(); - - $this->expectedCount = $expected; - } - - public function toString(): string - { - return \sprintf( - 'count matches %d', - $this->expectedCount - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - */ - protected function matches($other): bool - { - return $this->expectedCount === $this->getCountOf($other); - } - - /** - * @param iterable $other - */ - protected function getCountOf($other): ?int - { - if ($other instanceof Countable || \is_array($other)) { - return \count($other); - } - - if ($other instanceof Traversable) { - while ($other instanceof IteratorAggregate) { - $other = $other->getIterator(); - } - - $iterator = $other; - - if ($iterator instanceof Generator) { - return $this->getCountOfGenerator($iterator); - } - - if (!$iterator instanceof Iterator) { - return \iterator_count($iterator); - } - - $key = $iterator->key(); - $count = \iterator_count($iterator); - - // Manually rewind $iterator to previous key, since iterator_count - // moves pointer. - if ($key !== null) { - $iterator->rewind(); - - while ($iterator->valid() && $key !== $iterator->key()) { - $iterator->next(); - } - } - - return $count; - } - } - - /** - * Returns the total number of iterations from a generator. - * This will fully exhaust the generator. - */ - protected function getCountOfGenerator(Generator $generator): int - { - for ($count = 0; $generator->valid(); $generator->next()) { - ++$count; - } - - return $count; - } - - /** - * Returns the description of the failure. - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - 'actual size %d matches expected size %d', - $this->getCountOf($other), - $this->expectedCount - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Constraint that accepts any input value. - */ -class IsAnything extends Constraint -{ - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - return $returnResult ? true : null; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is anything'; - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - return 0; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that accepts null. - */ -class IsNull extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is null'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $other === null; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use ReflectionClass; - -/** - * Constraint that asserts that the class it is evaluated for has a given - * attribute. - * - * The attribute name is passed in the constructor. - */ -class ClassHasAttribute extends Constraint -{ - /** - * @var string - */ - private $attributeName; - - public function __construct(string $attributeName) - { - parent::__construct(); - - $this->attributeName = $attributeName; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return \sprintf( - 'has attribute "%s"', - $this->attributeName - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - $class = new ReflectionClass($other); - - return $class->hasProperty($this->attributeName); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - '%sclass "%s" %s', - \is_object($other) ? 'object of ' : '', - \is_object($other) ? \get_class($other) : $other, - $this->toString() - ); - } - - protected function attributeName(): string - { - return $this->attributeName; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Logical NOT. - */ -class LogicalNot extends Constraint -{ - /** - * @var Constraint - */ - private $constraint; - - public static function negate(string $string): string - { - $positives = [ - 'contains ', - 'exists', - 'has ', - 'is ', - 'are ', - 'matches ', - 'starts with ', - 'ends with ', - 'reference ', - 'not not ', - ]; - - $negatives = [ - 'does not contain ', - 'does not exist', - 'does not have ', - 'is not ', - 'are not ', - 'does not match ', - 'starts not with ', - 'ends not with ', - 'don\'t reference ', - 'not ', - ]; - - \preg_match('/(\'[\w\W]*\')([\w\W]*)("[\w\W]*")/i', $string, $matches); - - if (\count($matches) > 0) { - $nonInput = $matches[2]; - - $negatedString = \str_replace( - $nonInput, - \str_replace( - $positives, - $negatives, - $nonInput - ), - $string - ); - } else { - $negatedString = \str_replace( - $positives, - $negatives, - $string - ); - } - - return $negatedString; - } - - /** - * @param Constraint|mixed $constraint - */ - public function __construct($constraint) - { - parent::__construct(); - - if (!($constraint instanceof Constraint)) { - $constraint = new IsEqual($constraint); - } - - $this->constraint = $constraint; - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - $success = !$this->constraint->evaluate($other, $description, true); - - if ($returnResult) { - return $success; - } - - if (!$success) { - $this->fail($other, $description); - } - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - switch (\get_class($this->constraint)) { - case LogicalAnd::class: - case self::class: - case LogicalOr::class: - return 'not( ' . $this->constraint->toString() . ' )'; - - default: - return self::negate( - $this->constraint->toString() - ); - } - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - return \count($this->constraint); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - switch (\get_class($this->constraint)) { - case LogicalAnd::class: - case self::class: - case LogicalOr::class: - return 'not( ' . $this->constraint->failureDescription($other) . ' )'; - - default: - return self::negate( - $this->constraint->failureDescription($other) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Provides human readable messages for each JSON error. - */ -class JsonMatchesErrorMessageProvider -{ - /** - * Translates JSON error to a human readable string. - */ - public static function determineJsonError(string $error, string $prefix = ''): ?string - { - switch ($error) { - case \JSON_ERROR_NONE: - return null; - case \JSON_ERROR_DEPTH: - return $prefix . 'Maximum stack depth exceeded'; - case \JSON_ERROR_STATE_MISMATCH: - return $prefix . 'Underflow or the modes mismatch'; - case \JSON_ERROR_CTRL_CHAR: - return $prefix . 'Unexpected control character found'; - case \JSON_ERROR_SYNTAX: - return $prefix . 'Syntax error, malformed JSON'; - case \JSON_ERROR_UTF8: - return $prefix . 'Malformed UTF-8 characters, possibly incorrectly encoded'; - default: - return $prefix . 'Unknown error'; - } - } - - /** - * Translates a given type to a human readable message prefix. - */ - public static function translateTypeToPrefix(string $type): string - { - switch (\strtolower($type)) { - case 'expected': - $prefix = 'Expected value JSON decode error - '; - - break; - case 'actual': - $prefix = 'Actual value JSON decode error - '; - - break; - default: - $prefix = ''; - - break; - } - - return $prefix; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that accepts true. - */ -class IsTrue extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is true'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return $other === true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that the string it is evaluated for contains - * a given string. - * - * Uses mb_strpos() to find the position of the string in the input, if not - * found the evaluation fails. - * - * The sub-string is passed in the constructor. - */ -class StringContains extends Constraint -{ - /** - * @var string - */ - private $string; - - /** - * @var bool - */ - private $ignoreCase; - - public function __construct(string $string, bool $ignoreCase = false) - { - parent::__construct(); - - $this->string = $string; - $this->ignoreCase = $ignoreCase; - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - if ($this->ignoreCase) { - $string = \mb_strtolower($this->string); - } else { - $string = $this->string; - } - - return \sprintf( - 'contains "%s"', - $string - ); - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - if ('' === $this->string) { - return true; - } - - if ($this->ignoreCase) { - return \mb_stripos($other, $this->string) !== false; - } - - return \mb_strpos($other, $this->string) !== false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that asserts that a string is valid JSON. - */ -class IsJson extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'is valid JSON'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - if ($other === '') { - return false; - } - - \json_decode($other); - - if (\json_last_error()) { - return false; - } - - return true; - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - protected function failureDescription($other): string - { - if ($other === '') { - return 'an empty string is valid JSON'; - } - - \json_decode($other); - $error = JsonMatchesErrorMessageProvider::determineJsonError( - \json_last_error() - ); - - return \sprintf( - '%s is valid JSON (%s)', - $this->exporter->shortenedExport($other), - $error - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -/** - * Constraint that checks if the file(name) that it is evaluated for exists. - * - * The file path to check is passed as $other in evaluate(). - */ -class FileExists extends Constraint -{ - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - return 'file exists'; - } - - /** - * Evaluates the constraint for parameter $other. Returns true if the - * constraint is met, false otherwise. - * - * @param mixed $other value or object to evaluate - */ - protected function matches($other): bool - { - return \file_exists($other); - } - - /** - * Returns the description of the failure - * - * The beginning of failure messages is "Failed asserting that" in most - * cases. This method should return the second part of that sentence. - * - * @param mixed $other evaluated value or object - */ - protected function failureDescription($other): string - { - return \sprintf( - 'file "%s" exists', - $other - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\Constraint; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Logical AND. - */ -class LogicalAnd extends Constraint -{ - /** - * @var Constraint[] - */ - private $constraints = []; - - public static function fromConstraints(Constraint ...$constraints): self - { - $constraint = new self; - - $constraint->constraints = \array_values($constraints); - - return $constraint; - } - - /** - * @param Constraint[] $constraints - * - * @throws \PHPUnit\Framework\Exception - */ - public function setConstraints(array $constraints): void - { - $this->constraints = []; - - foreach ($constraints as $constraint) { - if (!($constraint instanceof Constraint)) { - throw new \PHPUnit\Framework\Exception( - 'All parameters to ' . __CLASS__ . - ' must be a constraint object.' - ); - } - - $this->constraints[] = $constraint; - } - } - - /** - * Evaluates the constraint for parameter $other - * - * If $returnResult is set to false (the default), an exception is thrown - * in case of a failure. null is returned otherwise. - * - * If $returnResult is true, the result of the evaluation is returned as - * a boolean value instead: true in case of success, false in case of a - * failure. - * - * @param mixed $other value or object to evaluate - * @param string $description Additional information about the test - * @param bool $returnResult Whether to return a result or throw an exception - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function evaluate($other, $description = '', $returnResult = false) - { - $success = true; - - foreach ($this->constraints as $constraint) { - if (!$constraint->evaluate($other, $description, true)) { - $success = false; - - break; - } - } - - if ($returnResult) { - return $success; - } - - if (!$success) { - $this->fail($other, $description); - } - } - - /** - * Returns a string representation of the constraint. - */ - public function toString(): string - { - $text = ''; - - foreach ($this->constraints as $key => $constraint) { - if ($key > 0) { - $text .= ' and '; - } - - $text .= $constraint->toString(); - } - - return $text; - } - - /** - * Counts the number of constraint elements. - */ - public function count(): int - { - $count = 0; - - foreach ($this->constraints as $constraint) { - $count += \count($constraint); - } - - return $count; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use DeepCopy\DeepCopy; -use PHPUnit\Framework\Constraint\Exception as ExceptionConstraint; -use PHPUnit\Framework\Constraint\ExceptionCode; -use PHPUnit\Framework\Constraint\ExceptionMessage; -use PHPUnit\Framework\Constraint\ExceptionMessageRegularExpression; -use PHPUnit\Framework\MockObject\Generator as MockGenerator; -use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount as AnyInvokedCountMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtIndex as InvokedAtIndexMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastCount as InvokedAtLeastCountMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtMostCount as InvokedAtMostCountMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; -use PHPUnit\Framework\MockObject\MockBuilder; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; -use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; -use PHPUnit\Runner\BaseTestRunner; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Util\GlobalState; -use PHPUnit\Util\PHP\AbstractPhpProcess; -use Prophecy; -use Prophecy\Exception\Prediction\PredictionException; -use Prophecy\Prophecy\MethodProphecy; -use Prophecy\Prophecy\ObjectProphecy; -use Prophecy\Prophet; -use ReflectionClass; -use ReflectionException; -use ReflectionObject; -use SebastianBergmann\Comparator\Comparator; -use SebastianBergmann\Comparator\Factory as ComparatorFactory; -use SebastianBergmann\Diff\Differ; -use SebastianBergmann\Exporter\Exporter; -use SebastianBergmann\GlobalState\Blacklist; -use SebastianBergmann\GlobalState\Restorer; -use SebastianBergmann\GlobalState\Snapshot; -use SebastianBergmann\ObjectEnumerator\Enumerator; -use Text_Template; -use Throwable; - -abstract class TestCase extends Assert implements Test, SelfDescribing -{ - private const LOCALE_CATEGORIES = [\LC_ALL, \LC_COLLATE, \LC_CTYPE, \LC_MONETARY, \LC_NUMERIC, \LC_TIME]; - - /** - * @var bool - */ - protected $backupGlobals; - - /** - * @var array - */ - protected $backupGlobalsBlacklist = []; - - /** - * @var bool - */ - protected $backupStaticAttributes; - - /** - * @var array - */ - protected $backupStaticAttributesBlacklist = []; - - /** - * @var bool - */ - protected $runTestInSeparateProcess; - - /** - * @var bool - */ - protected $preserveGlobalState = true; - - /** - * @var bool - */ - private $runClassInSeparateProcess; - - /** - * @var bool - */ - private $inIsolation = false; - - /** - * @var array - */ - private $data; - - /** - * @var string - */ - private $dataName; - - /** - * @var bool - */ - private $useErrorHandler; - - /** - * @var null|string - */ - private $expectedException; - - /** - * @var string - */ - private $expectedExceptionMessage; - - /** - * @var string - */ - private $expectedExceptionMessageRegExp; - - /** - * @var null|int|string - */ - private $expectedExceptionCode; - - /** - * @var string - */ - private $name; - - /** - * @var string[] - */ - private $dependencies = []; - - /** - * @var array - */ - private $dependencyInput = []; - - /** - * @var array - */ - private $iniSettings = []; - - /** - * @var array - */ - private $locale = []; - - /** - * @var array - */ - private $mockObjects = []; - - /** - * @var MockGenerator - */ - private $mockObjectGenerator; - - /** - * @var int - */ - private $status = BaseTestRunner::STATUS_UNKNOWN; - - /** - * @var string - */ - private $statusMessage = ''; - - /** - * @var int - */ - private $numAssertions = 0; - - /** - * @var TestResult - */ - private $result; - - /** - * @var mixed - */ - private $testResult; - - /** - * @var string - */ - private $output = ''; - - /** - * @var string - */ - private $outputExpectedRegex; - - /** - * @var string - */ - private $outputExpectedString; - - /** - * @var mixed - */ - private $outputCallback = false; - - /** - * @var bool - */ - private $outputBufferingActive = false; - - /** - * @var int - */ - private $outputBufferingLevel; - - /** - * @var Snapshot - */ - private $snapshot; - - /** - * @var Prophecy\Prophet - */ - private $prophet; - - /** - * @var bool - */ - private $beStrictAboutChangesToGlobalState = false; - - /** - * @var bool - */ - private $registerMockObjectsFromTestArgumentsRecursively = false; - - /** - * @var string[] - */ - private $warnings = []; - - /** - * @var array - */ - private $groups = []; - - /** - * @var bool - */ - private $doesNotPerformAssertions = false; - - /** - * @var Comparator[] - */ - private $customComparators = []; - - /** - * Returns a matcher that matches when the method is executed - * zero or more times. - */ - public static function any(): AnyInvokedCountMatcher - { - return new AnyInvokedCountMatcher; - } - - /** - * Returns a matcher that matches when the method is never executed. - */ - public static function never(): InvokedCountMatcher - { - return new InvokedCountMatcher(0); - } - - /** - * Returns a matcher that matches when the method is executed - * at least N times. - */ - public static function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher - { - return new InvokedAtLeastCountMatcher( - $requiredInvocations - ); - } - - /** - * Returns a matcher that matches when the method is executed at least once. - */ - public static function atLeastOnce(): InvokedAtLeastOnceMatcher - { - return new InvokedAtLeastOnceMatcher; - } - - /** - * Returns a matcher that matches when the method is executed exactly once. - */ - public static function once(): InvokedCountMatcher - { - return new InvokedCountMatcher(1); - } - - /** - * Returns a matcher that matches when the method is executed - * exactly $count times. - */ - public static function exactly(int $count): InvokedCountMatcher - { - return new InvokedCountMatcher($count); - } - - /** - * Returns a matcher that matches when the method is executed - * at most N times. - */ - public static function atMost(int $allowedInvocations): InvokedAtMostCountMatcher - { - return new InvokedAtMostCountMatcher($allowedInvocations); - } - - /** - * Returns a matcher that matches when the method is executed - * at the given index. - */ - public static function at(int $index): InvokedAtIndexMatcher - { - return new InvokedAtIndexMatcher($index); - } - - public static function returnValue($value): ReturnStub - { - return new ReturnStub($value); - } - - public static function returnValueMap(array $valueMap): ReturnValueMapStub - { - return new ReturnValueMapStub($valueMap); - } - - public static function returnArgument(int $argumentIndex): ReturnArgumentStub - { - return new ReturnArgumentStub($argumentIndex); - } - - public static function returnCallback($callback): ReturnCallbackStub - { - return new ReturnCallbackStub($callback); - } - - /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ - public static function returnSelf(): ReturnSelfStub - { - return new ReturnSelfStub; - } - - public static function throwException(Throwable $exception): ExceptionStub - { - return new ExceptionStub($exception); - } - - public static function onConsecutiveCalls(...$args): ConsecutiveCallsStub - { - return new ConsecutiveCallsStub($args); - } - - /** - * @param string $name - * @param string $dataName - */ - public function __construct($name = null, array $data = [], $dataName = '') - { - if ($name !== null) { - $this->setName($name); - } - - $this->data = $data; - $this->dataName = $dataName; - } - - /** - * This method is called before the first test of this test class is run. - */ - public static function setUpBeforeClass()/* The :void return type declaration that should be here would cause a BC issue */ - { - } - - /** - * This method is called after the last test of this test class is run. - */ - public static function tearDownAfterClass()/* The :void return type declaration that should be here would cause a BC issue */ - { - } - - /** - * This method is called before each test. - */ - protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */ - { - } - - /** - * This method is called after each test. - */ - protected function tearDown()/* The :void return type declaration that should be here would cause a BC issue */ - { - } - - /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \ReflectionException - */ - public function toString(): string - { - $class = new ReflectionClass($this); - - $buffer = \sprintf( - '%s::%s', - $class->name, - $this->getName(false) - ); - - return $buffer . $this->getDataSetAsString(); - } - - public function count(): int - { - return 1; - } - - public function getGroups(): array - { - return $this->groups; - } - - public function setGroups(array $groups): void - { - $this->groups = $groups; - } - - public function getAnnotations(): array - { - return \PHPUnit\Util\Test::parseTestMethodAnnotations( - \get_class($this), - $this->name - ); - } - - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function getName(bool $withDataSet = true): ?string - { - if ($withDataSet) { - return $this->name . $this->getDataSetAsString(false); - } - - return $this->name; - } - - /** - * Returns the size of the test. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function getSize(): int - { - return \PHPUnit\Util\Test::getSize( - \get_class($this), - $this->getName(false) - ); - } - - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function hasSize(): bool - { - return $this->getSize() !== \PHPUnit\Util\Test::UNKNOWN; - } - - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function isSmall(): bool - { - return $this->getSize() === \PHPUnit\Util\Test::SMALL; - } - - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function isMedium(): bool - { - return $this->getSize() === \PHPUnit\Util\Test::MEDIUM; - } - - /** - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function isLarge(): bool - { - return $this->getSize() === \PHPUnit\Util\Test::LARGE; - } - - public function getActualOutput(): string - { - if (!$this->outputBufferingActive) { - return $this->output; - } - - return \ob_get_contents(); - } - - public function hasOutput(): bool - { - if ($this->output === '') { - return false; - } - - if ($this->hasExpectationOnOutput()) { - return false; - } - - return true; - } - - public function doesNotPerformAssertions(): bool - { - return $this->doesNotPerformAssertions; - } - - public function expectOutputRegex(string $expectedRegex): void - { - $this->outputExpectedRegex = $expectedRegex; - } - - public function expectOutputString(string $expectedString): void - { - $this->outputExpectedString = $expectedString; - } - - public function hasExpectationOnOutput(): bool - { - return \is_string($this->outputExpectedString) || \is_string($this->outputExpectedRegex); - } - - public function getExpectedException(): ?string - { - return $this->expectedException; - } - - /** - * @return null|int|string - */ - public function getExpectedExceptionCode() - { - return $this->expectedExceptionCode; - } - - public function getExpectedExceptionMessage(): string - { - return $this->expectedExceptionMessage; - } - - public function getExpectedExceptionMessageRegExp(): string - { - return $this->expectedExceptionMessageRegExp; - } - - public function expectException(string $exception): void - { - $this->expectedException = $exception; - } - - /** - * @param int|string $code - */ - public function expectExceptionCode($code): void - { - $this->expectedExceptionCode = $code; - } - - public function expectExceptionMessage(string $message): void - { - $this->expectedExceptionMessage = $message; - } - - public function expectExceptionMessageRegExp(string $messageRegExp): void - { - $this->expectedExceptionMessageRegExp = $messageRegExp; - } - - /** - * Sets up an expectation for an exception to be raised by the code under test. - * Information for expected exception class, expected exception message, and - * expected exception code are retrieved from a given Exception object. - */ - public function expectExceptionObject(\Exception $exception): void - { - $this->expectException(\get_class($exception)); - $this->expectExceptionMessage($exception->getMessage()); - $this->expectExceptionCode($exception->getCode()); - } - - public function expectNotToPerformAssertions() - { - $this->doesNotPerformAssertions = true; - } - - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void - { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; - } - - public function setUseErrorHandler(bool $useErrorHandler): void - { - $this->useErrorHandler = $useErrorHandler; - } - - public function getStatus(): int - { - return $this->status; - } - - public function markAsRisky(): void - { - $this->status = BaseTestRunner::STATUS_RISKY; - } - - public function getStatusMessage(): string - { - return $this->statusMessage; - } - - public function hasFailed(): bool - { - $status = $this->getStatus(); - - return $status === BaseTestRunner::STATUS_FAILURE || $status === BaseTestRunner::STATUS_ERROR; - } - - /** - * Runs the test case and collects the results in a TestResult object. - * If no TestResult object is passed a new one will be created. - * - * @throws CodeCoverageException - * @throws ReflectionException - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException - * @throws \SebastianBergmann\CodeCoverage\RuntimeException - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function run(TestResult $result = null): TestResult - { - if ($result === null) { - $result = $this->createResult(); - } - - if (!$this instanceof WarningTestCase) { - $this->setTestResultObject($result); - $this->setUseErrorHandlerFromAnnotation(); - } - - if ($this->useErrorHandler !== null) { - $oldErrorHandlerSetting = $result->getConvertErrorsToExceptions(); - $result->convertErrorsToExceptions($this->useErrorHandler); - } - - if (!$this instanceof WarningTestCase && - !$this instanceof SkippedTestCase && - !$this->handleDependencies()) { - return $result; - } - - if ($this->runInSeparateProcess()) { - $runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess; - - $class = new ReflectionClass($this); - - if ($runEntireClass) { - $template = new Text_Template( - __DIR__ . '/../Util/PHP/Template/TestCaseClass.tpl' - ); - } else { - $template = new Text_Template( - __DIR__ . '/../Util/PHP/Template/TestCaseMethod.tpl' - ); - } - - if ($this->preserveGlobalState) { - $constants = GlobalState::getConstantsAsString(); - $globals = GlobalState::getGlobalsAsString(); - $includedFiles = GlobalState::getIncludedFilesAsString(); - $iniSettings = GlobalState::getIniSettingsAsString(); - } else { - $constants = ''; - - if (!empty($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - $globals = '$GLOBALS[\'__PHPUNIT_BOOTSTRAP\'] = ' . \var_export($GLOBALS['__PHPUNIT_BOOTSTRAP'], true) . ";\n"; - } else { - $globals = ''; - } - $includedFiles = ''; - $iniSettings = ''; - } - - $coverage = $result->getCollectCodeCoverageInformation() ? 'true' : 'false'; - $isStrictAboutTestsThatDoNotTestAnything = $result->isStrictAboutTestsThatDoNotTestAnything() ? 'true' : 'false'; - $isStrictAboutOutputDuringTests = $result->isStrictAboutOutputDuringTests() ? 'true' : 'false'; - $enforcesTimeLimit = $result->enforcesTimeLimit() ? 'true' : 'false'; - $isStrictAboutTodoAnnotatedTests = $result->isStrictAboutTodoAnnotatedTests() ? 'true' : 'false'; - $isStrictAboutResourceUsageDuringSmallTests = $result->isStrictAboutResourceUsageDuringSmallTests() ? 'true' : 'false'; - - if (\defined('PHPUNIT_COMPOSER_INSTALL')) { - $composerAutoload = \var_export(PHPUNIT_COMPOSER_INSTALL, true); - } else { - $composerAutoload = '\'\''; - } - - if (\defined('__PHPUNIT_PHAR__')) { - $phar = \var_export(__PHPUNIT_PHAR__, true); - } else { - $phar = '\'\''; - } - - if ($result->getCodeCoverage()) { - $codeCoverageFilter = $result->getCodeCoverage()->filter(); - } else { - $codeCoverageFilter = null; - } - - $data = \var_export(\serialize($this->data), true); - $dataName = \var_export($this->dataName, true); - $dependencyInput = \var_export(\serialize($this->dependencyInput), true); - $includePath = \var_export(\get_include_path(), true); - $codeCoverageFilter = \var_export(\serialize($codeCoverageFilter), true); - // must do these fixes because TestCaseMethod.tpl has unserialize('{data}') in it, and we can't break BC - // the lines above used to use addcslashes() rather than var_export(), which breaks null byte escape sequences - $data = "'." . $data . ".'"; - $dataName = "'.(" . $dataName . ").'"; - $dependencyInput = "'." . $dependencyInput . ".'"; - $includePath = "'." . $includePath . ".'"; - $codeCoverageFilter = "'." . $codeCoverageFilter . ".'"; - - $configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? ''; - - $var = [ - 'composerAutoload' => $composerAutoload, - 'phar' => $phar, - 'filename' => $class->getFileName(), - 'className' => $class->getName(), - 'collectCodeCoverageInformation' => $coverage, - 'data' => $data, - 'dataName' => $dataName, - 'dependencyInput' => $dependencyInput, - 'constants' => $constants, - 'globals' => $globals, - 'include_path' => $includePath, - 'included_files' => $includedFiles, - 'iniSettings' => $iniSettings, - 'isStrictAboutTestsThatDoNotTestAnything' => $isStrictAboutTestsThatDoNotTestAnything, - 'isStrictAboutOutputDuringTests' => $isStrictAboutOutputDuringTests, - 'enforcesTimeLimit' => $enforcesTimeLimit, - 'isStrictAboutTodoAnnotatedTests' => $isStrictAboutTodoAnnotatedTests, - 'isStrictAboutResourceUsageDuringSmallTests' => $isStrictAboutResourceUsageDuringSmallTests, - 'codeCoverageFilter' => $codeCoverageFilter, - 'configurationFilePath' => $configurationFilePath, - 'name' => $this->getName(false), - ]; - - if (!$runEntireClass) { - $var['methodName'] = $this->name; - } - - $template->setVar( - $var - ); - - $php = AbstractPhpProcess::factory(); - $php->runTestJob($template->render(), $this, $result); - } else { - $result->run($this); - } - - if (isset($oldErrorHandlerSetting)) { - $result->convertErrorsToExceptions($oldErrorHandlerSetting); - } - - $this->result = null; - - return $result; - } - - /** - * @throws \Throwable - */ - public function runBare(): void - { - $this->numAssertions = 0; - - $this->snapshotGlobalState(); - $this->startOutputBuffering(); - \clearstatcache(); - $currentWorkingDirectory = \getcwd(); - - $hookMethods = \PHPUnit\Util\Test::getHookMethods(\get_class($this)); - - $hasMetRequirements = false; - - try { - $this->checkRequirements(); - $hasMetRequirements = true; - - if ($this->inIsolation) { - foreach ($hookMethods['beforeClass'] as $method) { - $this->$method(); - } - } - - $this->setExpectedExceptionFromAnnotation(); - $this->setDoesNotPerformAssertionsFromAnnotation(); - - foreach ($hookMethods['before'] as $method) { - $this->$method(); - } - - $this->assertPreConditions(); - $this->testResult = $this->runTest(); - $this->verifyMockObjects(); - $this->assertPostConditions(); - - if (!empty($this->warnings)) { - throw new Warning( - \implode( - "\n", - \array_unique($this->warnings) - ) - ); - } - - $this->status = BaseTestRunner::STATUS_PASSED; - } catch (IncompleteTest $e) { - $this->status = BaseTestRunner::STATUS_INCOMPLETE; - $this->statusMessage = $e->getMessage(); - } catch (SkippedTest $e) { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->statusMessage = $e->getMessage(); - } catch (Warning $e) { - $this->status = BaseTestRunner::STATUS_WARNING; - $this->statusMessage = $e->getMessage(); - } catch (AssertionFailedError $e) { - $this->status = BaseTestRunner::STATUS_FAILURE; - $this->statusMessage = $e->getMessage(); - } catch (PredictionException $e) { - $this->status = BaseTestRunner::STATUS_FAILURE; - $this->statusMessage = $e->getMessage(); - } catch (Throwable $_e) { - $e = $_e; - $this->status = BaseTestRunner::STATUS_ERROR; - $this->statusMessage = $_e->getMessage(); - } - - $this->mockObjects = []; - $this->prophet = null; - - // Tear down the fixture. An exception raised in tearDown() will be - // caught and passed on when no exception was raised before. - try { - if ($hasMetRequirements) { - foreach ($hookMethods['after'] as $method) { - $this->$method(); - } - - if ($this->inIsolation) { - foreach ($hookMethods['afterClass'] as $method) { - $this->$method(); - } - } - } - } catch (Throwable $_e) { - $e = $e ?? $_e; - } - - try { - $this->stopOutputBuffering(); - } catch (RiskyTestError $_e) { - $e = $e ?? $_e; - } - - if (isset($_e)) { - $this->status = BaseTestRunner::STATUS_ERROR; - $this->statusMessage = $_e->getMessage(); - } - - \clearstatcache(); - - if ($currentWorkingDirectory != \getcwd()) { - \chdir($currentWorkingDirectory); - } - - $this->restoreGlobalState(); - $this->unregisterCustomComparators(); - $this->cleanupIniSettings(); - $this->cleanupLocaleSettings(); - - // Perform assertion on output. - if (!isset($e)) { - try { - if ($this->outputExpectedRegex !== null) { - $this->assertRegExp($this->outputExpectedRegex, $this->output); - } elseif ($this->outputExpectedString !== null) { - $this->assertEquals($this->outputExpectedString, $this->output); - } - } catch (Throwable $_e) { - $e = $_e; - } - } - - // Workaround for missing "finally". - if (isset($e)) { - if ($e instanceof PredictionException) { - $e = new AssertionFailedError($e->getMessage()); - } - - $this->onNotSuccessfulTest($e); - } - } - - public function setName(string $name): void - { - $this->name = $name; - } - - /** - * @param string[] $dependencies - */ - public function setDependencies(array $dependencies): void - { - $this->dependencies = $dependencies; - } - - public function getDependencies(): array - { - return $this->dependencies; - } - - public function hasDependencies(): bool - { - return \count($this->dependencies) > 0; - } - - public function setDependencyInput(array $dependencyInput): void - { - $this->dependencyInput = $dependencyInput; - } - - public function setBeStrictAboutChangesToGlobalState(?bool $beStrictAboutChangesToGlobalState): void - { - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - } - - public function setBackupGlobals(?bool $backupGlobals): void - { - if ($this->backupGlobals === null && $backupGlobals !== null) { - $this->backupGlobals = $backupGlobals; - } - } - - public function setBackupStaticAttributes(?bool $backupStaticAttributes): void - { - if ($this->backupStaticAttributes === null && $backupStaticAttributes !== null) { - $this->backupStaticAttributes = $backupStaticAttributes; - } - } - - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void - { - if ($this->runTestInSeparateProcess === null) { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; - } - } - - public function setRunClassInSeparateProcess(bool $runClassInSeparateProcess): void - { - if ($this->runClassInSeparateProcess === null) { - $this->runClassInSeparateProcess = $runClassInSeparateProcess; - } - } - - public function setPreserveGlobalState(bool $preserveGlobalState): void - { - $this->preserveGlobalState = $preserveGlobalState; - } - - public function setInIsolation(bool $inIsolation): void - { - $this->inIsolation = $inIsolation; - } - - public function isInIsolation(): bool - { - return $this->inIsolation; - } - - public function getResult() - { - return $this->testResult; - } - - public function setResult($result): void - { - $this->testResult = $result; - } - - public function setOutputCallback(callable $callback): void - { - $this->outputCallback = $callback; - } - - public function getTestResultObject(): ?TestResult - { - return $this->result; - } - - public function setTestResultObject(TestResult $result): void - { - $this->result = $result; - } - - public function registerMockObject(MockObject $mockObject): void - { - $this->mockObjects[] = $mockObject; - } - - /** - * Returns a builder object to create mock objects using a fluent interface. - * - * @param string|string[] $className - */ - public function getMockBuilder($className): MockBuilder - { - return new MockBuilder($this, $className); - } - - public function addToAssertionCount(int $count): void - { - $this->numAssertions += $count; - } - - /** - * Returns the number of assertions performed by this test. - */ - public function getNumAssertions(): int - { - return $this->numAssertions; - } - - public function usesDataProvider(): bool - { - return !empty($this->data); - } - - public function dataDescription(): string - { - return \is_string($this->dataName) ? $this->dataName : ''; - } - - /** - * @return int|string - */ - public function dataName() - { - return $this->dataName; - } - - public function registerComparator(Comparator $comparator): void - { - ComparatorFactory::getInstance()->register($comparator); - - $this->customComparators[] = $comparator; - } - - public function getDataSetAsString(bool $includeData = true): string - { - $buffer = ''; - - if (!empty($this->data)) { - if (\is_int($this->dataName)) { - $buffer .= \sprintf(' with data set #%d', $this->dataName); - } else { - $buffer .= \sprintf(' with data set "%s"', $this->dataName); - } - - $exporter = new Exporter; - - if ($includeData) { - $buffer .= \sprintf(' (%s)', $exporter->shortenedRecursiveExport($this->data)); - } - } - - return $buffer; - } - - /** - * Gets the data set of a TestCase. - */ - public function getProvidedData(): array - { - return $this->data; - } - - public function addWarning(string $warning): void - { - $this->warnings[] = $warning; - } - - /** - * Override to run the test and assert its state. - * - * @throws AssertionFailedError - * @throws Exception - * @throws ExpectationFailedException - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - * @throws Throwable - */ - protected function runTest() - { - if ($this->name === null) { - throw new Exception( - 'PHPUnit\Framework\TestCase::$name must not be null.' - ); - } - - $testArguments = \array_merge($this->data, $this->dependencyInput); - - $this->registerMockObjectsFromTestArguments($testArguments); - - try { - $testResult = $this->{$this->name}(...\array_values($testArguments)); - } catch (Throwable $exception) { - if (!$this->checkExceptionExpectations($exception)) { - throw $exception; - } - - if ($this->expectedException !== null) { - $this->assertThat( - $exception, - new ExceptionConstraint( - $this->expectedException - ) - ); - } - - if ($this->expectedExceptionMessage !== null) { - $this->assertThat( - $exception, - new ExceptionMessage( - $this->expectedExceptionMessage - ) - ); - } - - if ($this->expectedExceptionMessageRegExp !== null) { - $this->assertThat( - $exception, - new ExceptionMessageRegularExpression( - $this->expectedExceptionMessageRegExp - ) - ); - } - - if ($this->expectedExceptionCode !== null) { - $this->assertThat( - $exception, - new ExceptionCode( - $this->expectedExceptionCode - ) - ); - } - - return; - } - - if ($this->expectedException !== null) { - $this->assertThat( - null, - new ExceptionConstraint( - $this->expectedException - ) - ); - } elseif ($this->expectedExceptionMessage !== null) { - $this->numAssertions++; - - throw new AssertionFailedError( - \sprintf( - 'Failed asserting that exception with message "%s" is thrown', - $this->expectedExceptionMessage - ) - ); - } elseif ($this->expectedExceptionMessageRegExp !== null) { - $this->numAssertions++; - - throw new AssertionFailedError( - \sprintf( - 'Failed asserting that exception with message matching "%s" is thrown', - $this->expectedExceptionMessageRegExp - ) - ); - } elseif ($this->expectedExceptionCode !== null) { - $this->numAssertions++; - - throw new AssertionFailedError( - \sprintf( - 'Failed asserting that exception with code "%s" is thrown', - $this->expectedExceptionCode - ) - ); - } - - return $testResult; - } - - /** - * This method is a wrapper for the ini_set() function that automatically - * resets the modified php.ini setting to its original value after the - * test is run. - * - * @throws Exception - */ - protected function iniSet(string $varName, $newValue): void - { - $currentValue = \ini_set($varName, $newValue); - - if ($currentValue !== false) { - $this->iniSettings[$varName] = $currentValue; - } else { - throw new Exception( - \sprintf( - 'INI setting "%s" could not be set to "%s".', - $varName, - $newValue - ) - ); - } - } - - /** - * This method is a wrapper for the setlocale() function that automatically - * resets the locale to its original value after the test is run. - * - * @throws Exception - */ - protected function setLocale(...$args): void - { - if (\count($args) < 2) { - throw new Exception; - } - - [$category, $locale] = $args; - - if (\defined('LC_MESSAGES')) { - $categories[] = \LC_MESSAGES; - } - - if (!\in_array($category, self::LOCALE_CATEGORIES, true)) { - throw new Exception; - } - - if (!\is_array($locale) && !\is_string($locale)) { - throw new Exception; - } - - $this->locale[$category] = \setlocale($category, 0); - - $result = \setlocale(...$args); - - if ($result === false) { - throw new Exception( - 'The locale functionality is not implemented on your platform, ' . - 'the specified locale does not exist or the category name is ' . - 'invalid.' - ); - } - } - - /** - * Returns a test double for the specified class. - * - * @param string|string[] $originalClassName - * - * @throws Exception - * @throws \InvalidArgumentException - */ - protected function createMock($originalClassName): MockObject - { - return $this->getMockBuilder($originalClassName) - ->disableOriginalConstructor() - ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() - ->getMock(); - } - - /** - * Returns a configured test double for the specified class. - * - * @param string|string[] $originalClassName - * - * @throws Exception - * @throws \InvalidArgumentException - */ - protected function createConfiguredMock($originalClassName, array $configuration): MockObject - { - $o = $this->createMock($originalClassName); - - foreach ($configuration as $method => $return) { - $o->method($method)->willReturn($return); - } - - return $o; - } - - /** - * Returns a partial test double for the specified class. - * - * @param string|string[] $originalClassName - * @param string[] $methods - * - * @throws Exception - * @throws \InvalidArgumentException - */ - protected function createPartialMock($originalClassName, array $methods): MockObject - { - return $this->getMockBuilder($originalClassName) - ->disableOriginalConstructor() - ->disableOriginalClone() - ->disableArgumentCloning() - ->disallowMockingUnknownTypes() - ->setMethods(empty($methods) ? null : $methods) - ->getMock(); - } - - /** - * Returns a test proxy for the specified class. - * - * @throws Exception - * @throws \InvalidArgumentException - */ - protected function createTestProxy(string $originalClassName, array $constructorArguments = []): MockObject - { - return $this->getMockBuilder($originalClassName) - ->setConstructorArgs($constructorArguments) - ->enableProxyingToOriginalMethods() - ->getMock(); - } - - /** - * Mocks the specified class and returns the name of the mocked class. - * - * @param string $originalClassName - * @param array $methods - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param bool $cloneArguments - * - * @throws Exception - * @throws ReflectionException - * @throws \InvalidArgumentException - */ - protected function getMockClass($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = false, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false): string - { - $mock = $this->getMockObjectGenerator()->getMock( - $originalClassName, - $methods, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $cloneArguments - ); - - return \get_class($mock); - } - - /** - * Returns a mock object for the specified abstract class with all abstract - * methods of the class mocked. Concrete methods are not mocked by default. - * To mock concrete methods, use the 7th parameter ($mockedMethods). - * - * @param string $originalClassName - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param array $mockedMethods - * @param bool $cloneArguments - * - * @throws Exception - * @throws ReflectionException - * @throws \InvalidArgumentException - */ - protected function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject - { - $mockObject = $this->getMockObjectGenerator()->getMockForAbstractClass( - $originalClassName, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $mockedMethods, - $cloneArguments - ); - - $this->registerMockObject($mockObject); - - return $mockObject; - } - - /** - * Returns a mock object based on the given WSDL file. - * - * @param string $wsdlFile - * @param string $originalClassName - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param array $options An array of options passed to SOAPClient::_construct - * - * @throws Exception - * @throws ReflectionException - * @throws \InvalidArgumentException - */ - protected function getMockFromWsdl($wsdlFile, $originalClassName = '', $mockClassName = '', array $methods = [], $callOriginalConstructor = true, array $options = []): MockObject - { - if ($originalClassName === '') { - $fileName = \pathinfo(\basename(\parse_url(http://github.com/$wsdlFile)['path']), \PATHINFO_FILENAME); - $originalClassName = \preg_replace('/[^a-zA-Z0-9_]/', '', $fileName); - } - - if (!\class_exists($originalClassName)) { - eval( - $this->getMockObjectGenerator()->generateClassFromWsdl( - $wsdlFile, - $originalClassName, - $methods, - $options - ) - ); - } - - $mockObject = $this->getMockObjectGenerator()->getMock( - $originalClassName, - $methods, - ['', $options], - $mockClassName, - $callOriginalConstructor, - false, - false - ); - - $this->registerMockObject($mockObject); - - return $mockObject; - } - - /** - * Returns a mock object for the specified trait with all abstract methods - * of the trait mocked. Concrete methods to mock can be specified with the - * `$mockedMethods` parameter. - * - * @param string $traitName - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param array $mockedMethods - * @param bool $cloneArguments - * - * @throws Exception - * @throws ReflectionException - * @throws \InvalidArgumentException - */ - protected function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = false): MockObject - { - $mockObject = $this->getMockObjectGenerator()->getMockForTrait( - $traitName, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $mockedMethods, - $cloneArguments - ); - - $this->registerMockObject($mockObject); - - return $mockObject; - } - - /** - * Returns an object for the specified trait. - * - * @param string $traitName - * @param string $traitClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * - * @throws Exception - * @throws ReflectionException - * @throws \InvalidArgumentException - * - * @return object - */ - protected function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)/*: object*/ - { - return $this->getMockObjectGenerator()->getObjectForTrait( - $traitName, - $arguments, - $traitClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload - ); - } - - /** - * @param null|string $classOrInterface - * - * @throws Prophecy\Exception\Doubler\ClassNotFoundException - * @throws Prophecy\Exception\Doubler\DoubleException - * @throws Prophecy\Exception\Doubler\InterfaceNotFoundException - */ - protected function prophesize($classOrInterface = null): ObjectProphecy - { - return $this->getProphet()->prophesize($classOrInterface); - } - - /** - * Creates a default TestResult object. - */ - protected function createResult(): TestResult - { - return new TestResult; - } - - /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between setUp() and test. - */ - protected function assertPreConditions()/* The :void return type declaration that should be here would cause a BC issue */ - { - } - - /** - * Performs assertions shared by all tests of a test case. - * - * This method is called between test and tearDown(). - */ - protected function assertPostConditions()/* The :void return type declaration that should be here would cause a BC issue */ - { - } - - /** - * This method is called when a test method did not execute successfully. - * - * @throws Throwable - */ - protected function onNotSuccessfulTest(Throwable $t)/* The :void return type declaration that should be here would cause a BC issue */ - { - throw $t; - } - - private function setExpectedExceptionFromAnnotation(): void - { - try { - $expectedException = \PHPUnit\Util\Test::getExpectedException( - \get_class($this), - $this->name - ); - - if ($expectedException !== false) { - $this->expectException($expectedException['class']); - - if ($expectedException['code'] !== null) { - $this->expectExceptionCode($expectedException['code']); - } - - if ($expectedException['message'] !== '') { - $this->expectExceptionMessage($expectedException['message']); - } elseif ($expectedException['message_regex'] !== '') { - $this->expectExceptionMessageRegExp($expectedException['message_regex']); - } - } - } catch (ReflectionException $e) { - } - } - - private function setUseErrorHandlerFromAnnotation(): void - { - try { - $useErrorHandler = \PHPUnit\Util\Test::getErrorHandlerSettings( - \get_class($this), - $this->name - ); - - if ($useErrorHandler !== null) { - $this->setUseErrorHandler($useErrorHandler); - } - } catch (ReflectionException $e) { - } - } - - private function checkRequirements(): void - { - if (!$this->name || !\method_exists($this, $this->name)) { - return; - } - - $missingRequirements = \PHPUnit\Util\Test::getMissingRequirements( - \get_class($this), - $this->name - ); - - if (!empty($missingRequirements)) { - $this->markTestSkipped(\implode(\PHP_EOL, $missingRequirements)); - } - } - - private function verifyMockObjects(): void - { - foreach ($this->mockObjects as $mockObject) { - if ($mockObject->__phpunit_hasMatchers()) { - $this->numAssertions++; - } - - $mockObject->__phpunit_verify( - $this->shouldInvocationMockerBeReset($mockObject) - ); - } - - if ($this->prophet !== null) { - try { - $this->prophet->checkPredictions(); - } catch (Throwable $t) { - /* Intentionally left empty */ - } - - foreach ($this->prophet->getProphecies() as $objectProphecy) { - foreach ($objectProphecy->getMethodProphecies() as $methodProphecies) { - /** @var MethodProphecy[] $methodProphecies */ - foreach ($methodProphecies as $methodProphecy) { - $this->numAssertions += \count($methodProphecy->getCheckedPredictions()); - } - } - } - - if (isset($t)) { - throw $t; - } - } - } - - private function handleDependencies(): bool - { - if (!empty($this->dependencies) && !$this->inIsolation) { - $className = \get_class($this); - $passed = $this->result->passed(); - $passedKeys = \array_keys($passed); - $numKeys = \count($passedKeys); - - for ($i = 0; $i < $numKeys; $i++) { - $pos = \strpos($passedKeys[$i], ' with data set'); - - if ($pos !== false) { - $passedKeys[$i] = \substr($passedKeys[$i], 0, $pos); - } - } - - $passedKeys = \array_flip(\array_unique($passedKeys)); - - foreach ($this->dependencies as $dependency) { - $deepClone = false; - $shallowClone = false; - - if (\strpos($dependency, 'clone ') === 0) { - $deepClone = true; - $dependency = \substr($dependency, \strlen('clone ')); - } elseif (\strpos($dependency, '!clone ') === 0) { - $deepClone = false; - $dependency = \substr($dependency, \strlen('!clone ')); - } - - if (\strpos($dependency, 'shallowClone ') === 0) { - $shallowClone = true; - $dependency = \substr($dependency, \strlen('shallowClone ')); - } elseif (\strpos($dependency, '!shallowClone ') === 0) { - $shallowClone = false; - $dependency = \substr($dependency, \strlen('!shallowClone ')); - } - - if (\strpos($dependency, '::') === false) { - $dependency = $className . '::' . $dependency; - } - - if (!isset($passedKeys[$dependency])) { - if (!\is_callable($dependency, false, $callableName) || $dependency !== $callableName) { - $this->markWarningForUncallableDependency($dependency); - } else { - $this->markSkippedForMissingDependecy($dependency); - } - - return false; - } - - if (isset($passed[$dependency])) { - if ($passed[$dependency]['size'] != \PHPUnit\Util\Test::UNKNOWN && - $this->getSize() != \PHPUnit\Util\Test::UNKNOWN && - $passed[$dependency]['size'] > $this->getSize()) { - $this->result->addError( - $this, - new SkippedTestError( - 'This test depends on a test that is larger than itself.' - ), - 0 - ); - - return false; - } - - if ($deepClone) { - $deepCopy = new DeepCopy; - $deepCopy->skipUncloneable(false); - - $this->dependencyInput[$dependency] = $deepCopy->copy($passed[$dependency]['result']); - } elseif ($shallowClone) { - $this->dependencyInput[$dependency] = clone $passed[$dependency]['result']; - } else { - $this->dependencyInput[$dependency] = $passed[$dependency]['result']; - } - } else { - $this->dependencyInput[$dependency] = null; - } - } - } - - return true; - } - - private function markSkippedForMissingDependecy(string $dependency): void - { - $this->status = BaseTestRunner::STATUS_SKIPPED; - $this->result->startTest($this); - $this->result->addError( - $this, - new SkippedTestError( - \sprintf( - 'This test depends on "%s" to pass.', - $dependency - ) - ), - 0 - ); - $this->result->endTest($this, 0); - } - - private function markWarningForUncallableDependency(string $dependency): void - { - $this->status = BaseTestRunner::STATUS_WARNING; - $this->result->startTest($this); - $this->result->addWarning( - $this, - new Warning( - \sprintf( - 'This test depends on "%s" which does not exist.', - $dependency - ) - ), - 0 - ); - $this->result->endTest($this, 0); - } - - /** - * Get the mock object generator, creating it if it doesn't exist. - */ - private function getMockObjectGenerator(): MockGenerator - { - if ($this->mockObjectGenerator === null) { - $this->mockObjectGenerator = new MockGenerator; - } - - return $this->mockObjectGenerator; - } - - private function startOutputBuffering(): void - { - \ob_start(); - - $this->outputBufferingActive = true; - $this->outputBufferingLevel = \ob_get_level(); - } - - /** - * @throws RiskyTestError - */ - private function stopOutputBuffering(): void - { - if (\ob_get_level() !== $this->outputBufferingLevel) { - while (\ob_get_level() >= $this->outputBufferingLevel) { - \ob_end_clean(); - } - - throw new RiskyTestError( - 'Test code or tested code did not (only) close its own output buffers' - ); - } - - $this->output = \ob_get_contents(); - - if ($this->outputCallback !== false) { - $this->output = (string) \call_user_func($this->outputCallback, $this->output); - } - - \ob_end_clean(); - - $this->outputBufferingActive = false; - $this->outputBufferingLevel = \ob_get_level(); - } - - private function snapshotGlobalState(): void - { - if ($this->runTestInSeparateProcess || $this->inIsolation || - (!$this->backupGlobals === true && !$this->backupStaticAttributes)) { - return; - } - - $this->snapshot = $this->createGlobalStateSnapshot($this->backupGlobals === true); - } - - /** - * @throws RiskyTestError - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \InvalidArgumentException - */ - private function restoreGlobalState(): void - { - if (!$this->snapshot instanceof Snapshot) { - return; - } - - if ($this->beStrictAboutChangesToGlobalState) { - try { - $this->compareGlobalStateSnapshots( - $this->snapshot, - $this->createGlobalStateSnapshot($this->backupGlobals === true) - ); - } catch (RiskyTestError $rte) { - // Intentionally left empty - } - } - - $restorer = new Restorer; - - if ($this->backupGlobals === true) { - $restorer->restoreGlobalVariables($this->snapshot); - } - - if ($this->backupStaticAttributes) { - $restorer->restoreStaticAttributes($this->snapshot); - } - - $this->snapshot = null; - - if (isset($rte)) { - throw $rte; - } - } - - private function createGlobalStateSnapshot(bool $backupGlobals): Snapshot - { - $blacklist = new Blacklist; - - foreach ($this->backupGlobalsBlacklist as $globalVariable) { - $blacklist->addGlobalVariable($globalVariable); - } - - if (!\defined('PHPUNIT_TESTSUITE')) { - $blacklist->addClassNamePrefix('PHPUnit'); - $blacklist->addClassNamePrefix('SebastianBergmann\CodeCoverage'); - $blacklist->addClassNamePrefix('SebastianBergmann\FileIterator'); - $blacklist->addClassNamePrefix('SebastianBergmann\Invoker'); - $blacklist->addClassNamePrefix('SebastianBergmann\Timer'); - $blacklist->addClassNamePrefix('PHP_Token'); - $blacklist->addClassNamePrefix('Symfony'); - $blacklist->addClassNamePrefix('Text_Template'); - $blacklist->addClassNamePrefix('Doctrine\Instantiator'); - $blacklist->addClassNamePrefix('Prophecy'); - - foreach ($this->backupStaticAttributesBlacklist as $class => $attributes) { - foreach ($attributes as $attribute) { - $blacklist->addStaticAttribute($class, $attribute); - } - } - } - - return new Snapshot( - $blacklist, - $backupGlobals, - (bool) $this->backupStaticAttributes, - false, - false, - false, - false, - false, - false, - false - ); - } - - /** - * @throws RiskyTestError - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws \InvalidArgumentException - */ - private function compareGlobalStateSnapshots(Snapshot $before, Snapshot $after): void - { - $backupGlobals = $this->backupGlobals === null || $this->backupGlobals === true; - - if ($backupGlobals) { - $this->compareGlobalStateSnapshotPart( - $before->globalVariables(), - $after->globalVariables(), - "--- Global variables before the test\n+++ Global variables after the test\n" - ); - - $this->compareGlobalStateSnapshotPart( - $before->superGlobalVariables(), - $after->superGlobalVariables(), - "--- Super-global variables before the test\n+++ Super-global variables after the test\n" - ); - } - - if ($this->backupStaticAttributes) { - $this->compareGlobalStateSnapshotPart( - $before->staticAttributes(), - $after->staticAttributes(), - "--- Static attributes before the test\n+++ Static attributes after the test\n" - ); - } - } - - /** - * @throws RiskyTestError - */ - private function compareGlobalStateSnapshotPart(array $before, array $after, string $header): void - { - if ($before != $after) { - $differ = new Differ($header); - $exporter = new Exporter; - - $diff = $differ->diff( - $exporter->export($before), - $exporter->export($after) - ); - - throw new RiskyTestError( - $diff - ); - } - } - - private function getProphet(): Prophet - { - if ($this->prophet === null) { - $this->prophet = new Prophet; - } - - return $this->prophet; - } - - /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - */ - private function shouldInvocationMockerBeReset(MockObject $mock): bool - { - $enumerator = new Enumerator; - - foreach ($enumerator->enumerate($this->dependencyInput) as $object) { - if ($mock === $object) { - return false; - } - } - - if (!\is_array($this->testResult) && !\is_object($this->testResult)) { - return true; - } - - return !\in_array($mock, $enumerator->enumerate($this->testResult), true); - } - - /** - * @throws \SebastianBergmann\ObjectEnumerator\InvalidArgumentException - * @throws \SebastianBergmann\ObjectReflector\InvalidArgumentException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function registerMockObjectsFromTestArguments(array $testArguments, array &$visited = []): void - { - if ($this->registerMockObjectsFromTestArgumentsRecursively) { - $enumerator = new Enumerator; - - foreach ($enumerator->enumerate($testArguments) as $object) { - if ($object instanceof MockObject) { - $this->registerMockObject($object); - } - } - } else { - foreach ($testArguments as $testArgument) { - if ($testArgument instanceof MockObject) { - if ($this->isCloneable($testArgument)) { - $testArgument = clone $testArgument; - } - - $this->registerMockObject($testArgument); - } elseif (\is_array($testArgument) && !\in_array($testArgument, $visited, true)) { - $visited[] = $testArgument; - - $this->registerMockObjectsFromTestArguments( - $testArgument, - $visited - ); - } - } - } - } - - private function setDoesNotPerformAssertionsFromAnnotation(): void - { - $annotations = $this->getAnnotations(); - - if (isset($annotations['method']['doesNotPerformAssertions'])) { - $this->doesNotPerformAssertions = true; - } - } - - private function isCloneable(MockObject $testArgument): bool - { - $reflector = new ReflectionObject($testArgument); - - if (!$reflector->isCloneable()) { - return false; - } - - if ($reflector->hasMethod('__clone') && - $reflector->getMethod('__clone')->isPublic()) { - return true; - } - - return false; - } - - private function unregisterCustomComparators(): void - { - $factory = ComparatorFactory::getInstance(); - - foreach ($this->customComparators as $comparator) { - $factory->unregister($comparator); - } - - $this->customComparators = []; - } - - private function cleanupIniSettings(): void - { - foreach ($this->iniSettings as $varName => $oldValue) { - \ini_set($varName, $oldValue); - } - - $this->iniSettings = []; - } - - private function cleanupLocaleSettings(): void - { - foreach ($this->locale as $category => $locale) { - \setlocale($category, $locale); - } - - $this->locale = []; - } - - /** - * @throws ReflectionException - */ - private function checkExceptionExpectations(Throwable $throwable): bool - { - $result = false; - - if ($this->expectedException !== null || $this->expectedExceptionCode !== null || $this->expectedExceptionMessage !== null || $this->expectedExceptionMessageRegExp !== null) { - $result = true; - } - - if ($throwable instanceof Exception) { - $result = false; - } - - if (\is_string($this->expectedException)) { - $reflector = new ReflectionClass($this->expectedException); - - if ($this->expectedException === 'PHPUnit\Framework\Exception' || - $this->expectedException === '\PHPUnit\Framework\Exception' || - $reflector->isSubclassOf(Exception::class)) { - $result = true; - } - } - - return $result; - } - - private function runInSeparateProcess(): bool - { - return ($this->runTestInSeparateProcess === true || $this->runClassInSeparateProcess === true) && - $this->inIsolation !== true && !$this instanceof PhptTestCase; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -class BadMethodCallException extends \BadMethodCallException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -class RuntimeException extends \RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * Interface for exceptions used by PHPUnit_MockObject. - */ -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit_Framework_MockObject_MockObject; - -interface MockObject extends PHPUnit_Framework_MockObject_MockObject -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use Exception; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker; -use PHPUnit\Framework\MockObject\Builder\Match; -use PHPUnit\Framework\MockObject\Builder\NamespaceMatch; -use PHPUnit\Framework\MockObject\Matcher\DeferredError; -use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation; -use PHPUnit\Framework\MockObject\Stub\MatcherCollection; - -/** - * Mocker for invocations which are sent from - * MockObject objects. - * - * Keeps track of all expectations and stubs as well as registering - * identifications for builders. - */ -class InvocationMocker implements MatcherCollection, Invokable, NamespaceMatch -{ - /** - * @var MatcherInvocation[] - */ - private $matchers = []; - - /** - * @var Match[] - */ - private $builderMap = []; - - /** - * @var string[] - */ - private $configurableMethods; - - /** - * @var bool - */ - private $returnValueGeneration; - - public function __construct(array $configurableMethods, bool $returnValueGeneration) - { - $this->configurableMethods = $configurableMethods; - $this->returnValueGeneration = $returnValueGeneration; - } - - public function addMatcher(MatcherInvocation $matcher): void - { - $this->matchers[] = $matcher; - } - - public function hasMatchers() - { - foreach ($this->matchers as $matcher) { - if ($matcher->hasMatchers()) { - return true; - } - } - - return false; - } - - /** - * @return null|bool - */ - public function lookupId($id) - { - if (isset($this->builderMap[$id])) { - return $this->builderMap[$id]; - } - } - - /** - * @throws RuntimeException - */ - public function registerId($id, Match $builder): void - { - if (isset($this->builderMap[$id])) { - throw new RuntimeException( - 'Match builder with id <' . $id . '> is already registered.' - ); - } - - $this->builderMap[$id] = $builder; - } - - /** - * @return BuilderInvocationMocker - */ - public function expects(MatcherInvocation $matcher) - { - return new BuilderInvocationMocker( - $this, - $matcher, - $this->configurableMethods - ); - } - - /** - * @throws Exception - */ - public function invoke(Invocation $invocation) - { - $exception = null; - $hasReturnValue = false; - $returnValue = null; - - foreach ($this->matchers as $match) { - try { - if ($match->matches($invocation)) { - $value = $match->invoked($invocation); - - if (!$hasReturnValue) { - $returnValue = $value; - $hasReturnValue = true; - } - } - } catch (Exception $e) { - $exception = $e; - } - } - - if ($exception !== null) { - throw $exception; - } - - if ($hasReturnValue) { - return $returnValue; - } - - if ($this->returnValueGeneration === false) { - $exception = new ExpectationFailedException( - \sprintf( - 'Return value inference disabled and no expectation set up for %s::%s()', - $invocation->getClassName(), - $invocation->getMethodName() - ) - ); - - if (\strtolower($invocation->getMethodName()) === '__tostring') { - $this->addMatcher(new DeferredError($exception)); - - return ''; - } - - throw $exception; - } - - return $invocation->generateReturnValue(); - } - - /** - * @return bool - */ - public function matches(Invocation $invocation) - { - foreach ($this->matchers as $matcher) { - if (!$matcher->matches($invocation)) { - return false; - } - } - - return true; - } - - /** - * @throws \PHPUnit\Framework\ExpectationFailedException - * - * @return bool - */ - public function verify() - { - foreach ($this->matchers as $matcher) { - $matcher->verify(); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\SelfDescribing; - -/** - * An object that stubs the process of a normal method for a mock object. - * - * The stub object will replace the code for the stubbed method and return a - * specific value instead of the original value. - */ -interface Stub extends SelfDescribing -{ - /** - * Fakes the processing of the invocation $invocation by returning a - * specific value. - * - * @param Invocation $invocation The invocation which was mocked and matched by the current method and argument matchers - */ - public function invoke(Invocation $invocation); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Interface for classes which must verify a given expectation. - */ -interface Verifiable -{ - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function verify(); -} - - public function {method_name}({arguments}) - { - } - - @trigger_error({deprecation}, E_USER_DEPRECATED); - - public function method() - { - $any = new \PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount; - $expects = $this->expects($any); - - return call_user_func_array([$expects, 'method'], func_get_args()); - } -{namespace}class {class_name} extends \SoapClient -{ - public function __construct($wsdl, array $options) - { - parent::__construct('{wsdl}', $options); - } -{methods}} -{prologue}class {class_name} -{ - use {trait_name}; -} - - {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} - { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationMocker()->invoke( - new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - - return call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); - } - - {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} - { - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationMocker()->invoke( - new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - - call_user_func_array(array($this->__phpunit_originalObject, "{method_name}"), $__phpunit_arguments); - } - - {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $this->__phpunit_getInvocationMocker()->invoke( - new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - } -{prologue}{class_declaration} -{ - private $__phpunit_invocationMocker; - private $__phpunit_originalObject; - private $__phpunit_configurable = {configurable}; - private $__phpunit_returnValueGeneration = true; - -{clone}{mocked_methods} - public function expects(\PHPUnit\Framework\MockObject\Matcher\Invocation $matcher) - { - return $this->__phpunit_getInvocationMocker()->expects($matcher); - } -{method} - public function __phpunit_setOriginalObject($originalObject) - { - $this->__phpunit_originalObject = $originalObject; - } - - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) - { - $this->__phpunit_returnValueGeneration = $returnValueGeneration; - } - - public function __phpunit_getInvocationMocker() - { - if ($this->__phpunit_invocationMocker === null) { - $this->__phpunit_invocationMocker = new \PHPUnit\Framework\MockObject\InvocationMocker($this->__phpunit_configurable, $this->__phpunit_returnValueGeneration); - } - - return $this->__phpunit_invocationMocker; - } - - public function __phpunit_hasMatchers() - { - return $this->__phpunit_getInvocationMocker()->hasMatchers(); - } - - public function __phpunit_verify(bool $unsetInvocationMocker = true) - { - $this->__phpunit_getInvocationMocker()->verify(); - - if ($unsetInvocationMocker) { - $this->__phpunit_invocationMocker = null; - } - } -}{epilogue} - - {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} - {{deprecation} - $__phpunit_arguments = [{arguments_call}]; - $__phpunit_count = func_num_args(); - - if ($__phpunit_count > {arguments_count}) { - $__phpunit_arguments_tmp = func_get_args(); - - for ($__phpunit_i = {arguments_count}; $__phpunit_i < $__phpunit_count; $__phpunit_i++) { - $__phpunit_arguments[] = $__phpunit_arguments_tmp[$__phpunit_i]; - } - } - - $__phpunit_result = $this->__phpunit_getInvocationMocker()->invoke( - new \PHPUnit\Framework\MockObject\Invocation\ObjectInvocation( - '{class_name}', '{method_name}', $__phpunit_arguments, '{return_type}', $this, {clone_arguments} - ) - ); - - return $__phpunit_result; - } - - {modifier} function {reference}{method_name}({arguments_decl}){return_delim}{return_type} - { - throw new \PHPUnit\Framework\MockObject\BadMethodCallException('Static method "{method_name}" cannot be invoked on mock object'); - } - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); - } - public function __clone() - { - $this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker(); - parent::__clone(); - } - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * Interface for invocations. - */ -interface Invocation -{ - /** - * @return mixed mocked return value - */ - public function generateReturnValue(); - - public function getClassName(): string; - - public function getMethodName(): string; - - public function getParameters(): array; - - public function getReturnType(): string; - - public function isReturnTypeNullable(): bool; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Invocation; - -use PHPUnit\Framework\MockObject\Generator; -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\SelfDescribing; -use ReflectionObject; -use SebastianBergmann\Exporter\Exporter; - -/** - * Represents a static invocation. - */ -class StaticInvocation implements Invocation, SelfDescribing -{ - /** - * @var array - */ - private static $uncloneableExtensions = [ - 'mysqli' => true, - 'SQLite' => true, - 'sqlite3' => true, - 'tidy' => true, - 'xmlwriter' => true, - 'xsl' => true, - ]; - - /** - * @var array - */ - private static $uncloneableClasses = [ - 'Closure', - 'COMPersistHelper', - 'IteratorIterator', - 'RecursiveIteratorIterator', - 'SplFileObject', - 'PDORow', - 'ZipArchive', - ]; - - /** - * @var string - */ - private $className; - - /** - * @var string - */ - private $methodName; - - /** - * @var array - */ - private $parameters; - - /** - * @var string - */ - private $returnType; - - /** - * @var bool - */ - private $isReturnTypeNullable = false; - - /** - * @param string $className - * @param string $methodName - * @param string $returnType - * @param bool $cloneObjects - */ - public function __construct($className, $methodName, array $parameters, $returnType, $cloneObjects = false) - { - $this->className = $className; - $this->methodName = $methodName; - $this->parameters = $parameters; - - if (\strtolower($methodName) === '__tostring') { - $returnType = 'string'; - } - - if (\strpos($returnType, '?') === 0) { - $returnType = \substr($returnType, 1); - $this->isReturnTypeNullable = true; - } - - $this->returnType = $returnType; - - if (!$cloneObjects) { - return; - } - - foreach ($this->parameters as $key => $value) { - if (\is_object($value)) { - $this->parameters[$key] = $this->cloneObject($value); - } - } - } - - public function getClassName(): string - { - return $this->className; - } - - public function getMethodName(): string - { - return $this->methodName; - } - - public function getParameters(): array - { - return $this->parameters; - } - - public function getReturnType(): string - { - return $this->returnType; - } - - public function isReturnTypeNullable(): bool - { - return $this->isReturnTypeNullable; - } - - /** - * @throws \ReflectionException - * @throws \PHPUnit\Framework\MockObject\RuntimeException - * @throws \PHPUnit\Framework\Exception - * - * @return mixed Mocked return value - */ - public function generateReturnValue() - { - if ($this->isReturnTypeNullable) { - return; - } - - switch (\strtolower($this->returnType)) { - case '': - case 'void': - return; - - case 'string': - return ''; - - case 'float': - return 0.0; - - case 'int': - return 0; - - case 'bool': - return false; - - case 'array': - return []; - - case 'object': - return new \stdClass; - - case 'callable': - case 'closure': - return function (): void { - }; - - case 'traversable': - case 'generator': - case 'iterable': - $generator = function () { - yield; - }; - - return $generator(); - - default: - $generator = new Generator; - - return $generator->getMock($this->returnType, [], [], '', false); - } - } - - public function toString(): string - { - $exporter = new Exporter; - - return \sprintf( - '%s::%s(%s)%s', - $this->className, - $this->methodName, - \implode( - ', ', - \array_map( - [$exporter, 'shortenedExport'], - $this->parameters - ) - ), - $this->returnType ? \sprintf(': %s', $this->returnType) : '' - ); - } - - /** - * @param object $original - * - * @return object - */ - private function cloneObject($original) - { - $cloneable = null; - $object = new ReflectionObject($original); - - // Check the blacklist before asking PHP reflection to work around - // https://bugs.php.net/bug.php?id=53967 - if ($object->isInternal() && - isset(self::$uncloneableExtensions[$object->getExtensionName()])) { - $cloneable = false; - } - - if ($cloneable === null) { - foreach (self::$uncloneableClasses as $class) { - if ($original instanceof $class) { - $cloneable = false; - - break; - } - } - } - - if ($cloneable === null) { - $cloneable = $object->isCloneable(); - } - - if ($cloneable === null && $object->hasMethod('__clone')) { - $method = $object->getMethod('__clone'); - $cloneable = $method->isPublic(); - } - - if ($cloneable === null) { - $cloneable = true; - } - - if ($cloneable) { - try { - return clone $original; - } catch (\Exception $e) { - return $original; - } - } else { - return $original; - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Invocation; - -/** - * Represents a non-static invocation. - */ -class ObjectInvocation extends StaticInvocation -{ - /** - * @var object - */ - private $object; - - /** - * @param string $className - * @param string $methodName - * @param string $returnType - * @param object $object - * @param bool $cloneObjects - */ - public function __construct($className, $methodName, array $parameters, $returnType, $object, $cloneObjects = false) - { - parent::__construct($className, $methodName, $parameters, $returnType, $cloneObjects); - - $this->object = $object; - } - - public function getObject() - { - return $this->object; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use ReflectionClass; -use ReflectionException; -use ReflectionMethod; -use Text_Template; - -final class MockMethod -{ - /** - * @var Text_Template[] - */ - private static $templates = []; - - /** - * @var string - */ - private $className; - - /** - * @var string - */ - private $methodName; - - /** - * @var bool - */ - private $cloneArguments; - - /** - * @var string string - */ - private $modifier; - - /** - * @var string - */ - private $argumentsForDeclaration; - - /** - * @var string - */ - private $argumentsForCall; - - /** - * @var string - */ - private $returnType; - - /** - * @var string - */ - private $reference; - - /** - * @var bool - */ - private $callOriginalMethod; - - /** - * @var bool - */ - private $static; - - /** - * @var ?string - */ - private $deprecation; - - /** - * @var bool - */ - private $allowsReturnNull; - - public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self - { - if ($method->isPrivate()) { - $modifier = 'private'; - } elseif ($method->isProtected()) { - $modifier = 'protected'; - } else { - $modifier = 'public'; - } - - if ($method->isStatic()) { - $modifier .= ' static'; - } - - if ($method->returnsReference()) { - $reference = '&'; - } else { - $reference = ''; - } - - if ($method->hasReturnType()) { - $returnType = (string) $method->getReturnType(); - } else { - $returnType = ''; - } - - $docComment = $method->getDocComment(); - - if (\is_string($docComment) - && \preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation) - ) { - $deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1])); - } else { - $deprecation = null; - } - - return new self( - $method->getDeclaringClass()->getName(), - $method->getName(), - $cloneArguments, - $modifier, - self::getMethodParameters($method), - self::getMethodParameters($method, true), - $returnType, - $reference, - $callOriginalMethod, - $method->isStatic(), - $deprecation, - $method->hasReturnType() && $method->getReturnType()->allowsNull() - ); - } - - public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self - { - return new self( - $fullClassName, - $methodName, - $cloneArguments, - 'public', - '', - '', - '', - '', - false, - false, - null, - false - ); - } - - public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, string $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation, bool $allowsReturnNull) - { - $this->className = $className; - $this->methodName = $methodName; - $this->cloneArguments = $cloneArguments; - $this->modifier = $modifier; - $this->argumentsForDeclaration = $argumentsForDeclaration; - $this->argumentsForCall = $argumentsForCall; - $this->returnType = $returnType; - $this->reference = $reference; - $this->callOriginalMethod = $callOriginalMethod; - $this->static = $static; - $this->deprecation = $deprecation; - $this->allowsReturnNull = $allowsReturnNull; - } - - public function getName(): string - { - return $this->methodName; - } - - /** - * @throws \ReflectionException - * @throws \PHPUnit\Framework\MockObject\RuntimeException - * @throws \InvalidArgumentException - */ - public function generateCode(): string - { - if ($this->static) { - $templateFile = 'mocked_static_method.tpl'; - } elseif ($this->returnType === 'void') { - $templateFile = \sprintf( - '%s_method_void.tpl', - $this->callOriginalMethod ? 'proxied' : 'mocked' - ); - } else { - $templateFile = \sprintf( - '%s_method.tpl', - $this->callOriginalMethod ? 'proxied' : 'mocked' - ); - } - - $returnType = $this->returnType; - // @see https://bugs.php.net/bug.php?id=70722 - if ($returnType === 'self') { - $returnType = $this->className; - } - - // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/406 - if ($returnType === 'parent') { - $reflector = new ReflectionClass($this->className); - - $parentClass = $reflector->getParentClass(); - - if ($parentClass === false) { - throw new RuntimeException( - \sprintf( - 'Cannot mock %s::%s because "parent" return type declaration is used but %s does not have a parent class', - $this->className, - $this->methodName, - $this->className - ) - ); - } - - $returnType = $parentClass->getName(); - } - - $deprecation = $this->deprecation; - - if (null !== $this->deprecation) { - $deprecation = "The $this->className::$this->methodName method is deprecated ($this->deprecation)."; - $deprecationTemplate = $this->getTemplate('deprecation.tpl'); - - $deprecationTemplate->setVar([ - 'deprecation' => \var_export($deprecation, true), - ]); - - $deprecation = $deprecationTemplate->render(); - } - - $template = $this->getTemplate($templateFile); - - $template->setVar( - [ - 'arguments_decl' => $this->argumentsForDeclaration, - 'arguments_call' => $this->argumentsForCall, - 'return_delim' => $returnType ? ': ' : '', - 'return_type' => $this->allowsReturnNull ? '?' . $returnType : $returnType, - 'arguments_count' => !empty($this->argumentsForCall) ? \substr_count($this->argumentsForCall, ',') + 1 : 0, - 'class_name' => $this->className, - 'method_name' => $this->methodName, - 'modifier' => $this->modifier, - 'reference' => $this->reference, - 'clone_arguments' => $this->cloneArguments ? 'true' : 'false', - 'deprecation' => $deprecation, - ] - ); - - return $template->render(); - } - - private function getTemplate(string $template): Text_Template - { - $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; - - if (!isset(self::$templates[$filename])) { - self::$templates[$filename] = new Text_Template($filename); - } - - return self::$templates[$filename]; - } - - /** - * Returns the parameters of a function or method. - * - * @throws RuntimeException - */ - private static function getMethodParameters(ReflectionMethod $method, bool $forCall = false): string - { - $parameters = []; - - foreach ($method->getParameters() as $i => $parameter) { - $name = '$' . $parameter->getName(); - - /* Note: PHP extensions may use empty names for reference arguments - * or "..." for methods taking a variable number of arguments. - */ - if ($name === '$' || $name === '$...') { - $name = '$arg' . $i; - } - - if ($parameter->isVariadic()) { - if ($forCall) { - continue; - } - - $name = '...' . $name; - } - - $nullable = ''; - $default = ''; - $reference = ''; - $typeDeclaration = ''; - - if (!$forCall) { - if ($parameter->hasType() && $parameter->allowsNull()) { - $nullable = '?'; - } - - if ($parameter->hasType() && (string) $parameter->getType() !== 'self') { - $typeDeclaration = $parameter->getType() . ' '; - } else { - try { - $class = $parameter->getClass(); - } catch (ReflectionException $e) { - throw new RuntimeException( - \sprintf( - 'Cannot mock %s::%s() because a class or ' . - 'interface used in the signature is not loaded', - $method->getDeclaringClass()->getName(), - $method->getName() - ), - 0, - $e - ); - } - - if ($class !== null) { - $typeDeclaration = $class->getName() . ' '; - } - } - - if (!$parameter->isVariadic()) { - if ($parameter->isDefaultValueAvailable()) { - $value = $parameter->getDefaultValueConstantName(); - - if ($value === null) { - $value = \var_export($parameter->getDefaultValue(), true); - } elseif (!\defined($value)) { - $rootValue = \preg_replace('/^.*\\\\/', '', $value); - $value = \defined($rootValue) ? $rootValue : $value; - } - - $default = ' = ' . $value; - } elseif ($parameter->isOptional()) { - $default = ' = null'; - } - } - } - - if ($parameter->isPassedByReference()) { - $reference = '&'; - } - - $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default; - } - - return \implode(', ', $parameters); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -/** - * Interface for classes which can be invoked. - * - * The invocation will be taken from a mock object and passed to an object - * of this class. - */ -interface Invokable extends Verifiable -{ - /** - * Invokes the invocation object $invocation so that it can be checked for - * expectations or matched against stubs. - * - * @param Invocation $invocation The invocation object passed from mock object - * - * @return object - */ - public function invoke(Invocation $invocation); - - /** - * Checks if the invocation matches. - * - * @param Invocation $invocation The invocation object passed from mock object - * - * @return bool - */ - public function matches(Invocation $invocation); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Invocation matcher which allows any parameters to a method. - */ -class AnyParameters extends StatelessInvocation -{ - public function toString(): string - { - return 'with any parameters'; - } - - /** - * @return bool - */ - public function matches(BaseInvocation $invocation) - { - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Invocation matcher which checks if a method has been invoked at least one - * time. - * - * If the number of invocations is 0 it will throw an exception in verify. - */ -class InvokedAtLeastOnce extends InvokedRecorder -{ - public function toString(): string - { - return 'invoked at least once'; - } - - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function verify(): void - { - $count = $this->getInvocationCount(); - - if ($count < 1) { - throw new ExpectationFailedException( - 'Expected invocation at least once but it never occurred.' - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsAnything; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Invocation matcher which looks for specific parameters in the invocations. - * - * Checks the parameters of all incoming invocations, the parameter list is - * checked against the defined constraints in $parameters. If the constraint - * is met it will return true in matches(). - */ -class Parameters extends StatelessInvocation -{ - /** - * @var Constraint[] - */ - private $parameters = []; - - /** - * @var BaseInvocation - */ - private $invocation; - - /** - * @var ExpectationFailedException - */ - private $parameterVerificationResult; - - /** - * @throws \PHPUnit\Framework\Exception - */ - public function __construct(array $parameters) - { - foreach ($parameters as $parameter) { - if (!($parameter instanceof Constraint)) { - $parameter = new IsEqual( - $parameter - ); - } - - $this->parameters[] = $parameter; - } - } - - public function toString(): string - { - $text = 'with parameter'; - - foreach ($this->parameters as $index => $parameter) { - if ($index > 0) { - $text .= ' and'; - } - - $text .= ' ' . $index . ' ' . $parameter->toString(); - } - - return $text; - } - - /** - * @throws \Exception - * - * @return bool - */ - public function matches(BaseInvocation $invocation) - { - $this->invocation = $invocation; - $this->parameterVerificationResult = null; - - try { - $this->parameterVerificationResult = $this->verify(); - - return $this->parameterVerificationResult; - } catch (ExpectationFailedException $e) { - $this->parameterVerificationResult = $e; - - throw $this->parameterVerificationResult; - } - } - - /** - * Checks if the invocation $invocation matches the current rules. If it - * does the matcher will get the invoked() method called which should check - * if an expectation is met. - * - * @throws ExpectationFailedException - * - * @return bool - */ - public function verify() - { - if (isset($this->parameterVerificationResult)) { - return $this->guardAgainstDuplicateEvaluationOfParameterConstraints(); - } - - if ($this->invocation === null) { - throw new ExpectationFailedException('Mocked method does not exist.'); - } - - if (\count($this->invocation->getParameters()) < \count($this->parameters)) { - $message = 'Parameter count for invocation %s is too low.'; - - // The user called `->with($this->anything())`, but may have meant - // `->withAnyParameters()`. - // - // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/199 - if (\count($this->parameters) === 1 && - \get_class($this->parameters[0]) === IsAnything::class) { - $message .= "\nTo allow 0 or more parameters with any value, omit ->with() or use ->withAnyParameters() instead."; - } - - throw new ExpectationFailedException( - \sprintf($message, $this->invocation->toString()) - ); - } - - foreach ($this->parameters as $i => $parameter) { - $parameter->evaluate( - $this->invocation->getParameters()[$i], - \sprintf( - 'Parameter %s for invocation %s does not match expected ' . - 'value.', - $i, - $this->invocation->toString() - ) - ); - } - - return true; - } - - /** - * @throws ExpectationFailedException - * - * @return bool - */ - private function guardAgainstDuplicateEvaluationOfParameterConstraints() - { - if ($this->parameterVerificationResult instanceof \Exception) { - throw $this->parameterVerificationResult; - } - - return (bool) $this->parameterVerificationResult; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Invocation matcher which does not care about previous state from earlier - * invocations. - * - * This abstract class can be implemented by matchers which does not care about - * state but only the current run-time value of the invocation itself. - */ -abstract class StatelessInvocation implements Invocation -{ - /** - * Registers the invocation $invocation in the object as being invoked. - * This will only occur after matches() returns true which means the - * current invocation is the correct one. - * - * The matcher can store information from the invocation which can later - * be checked in verify(), or it can check the values directly and throw - * and exception if an expectation is not met. - * - * If the matcher is a stub it will also have a return value. - * - * @param BaseInvocation $invocation Object containing information on a mocked or stubbed method which was invoked - */ - public function invoked(BaseInvocation $invocation) - { - } - - /** - * Checks if the invocation $invocation matches the current rules. If it does - * the matcher will get the invoked() method called which should check if an - * expectation is met. - * - * @return bool - */ - public function verify() - { - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -use PHPUnit\Util\InvalidArgumentHelper; - -/** - * Invocation matcher which looks for a specific method name in the invocations. - * - * Checks the method name all incoming invocations, the name is checked against - * the defined constraint $constraint. If the constraint is met it will return - * true in matches(). - */ -class MethodName extends StatelessInvocation -{ - /** - * @var Constraint - */ - private $constraint; - - /** - * @param Constraint|string - * - * @throws Constraint - * @throws \PHPUnit\Framework\Exception - */ - public function __construct($constraint) - { - if (!$constraint instanceof Constraint) { - if (!\is_string($constraint)) { - throw InvalidArgumentHelper::factory(1, 'string'); - } - - $constraint = new IsEqual( - $constraint, - 0, - 10, - false, - true - ); - } - - $this->constraint = $constraint; - } - - public function toString(): string - { - return 'method name ' . $this->constraint->toString(); - } - - /** - * @return bool - */ - public function matches(BaseInvocation $invocation) - { - return $this->constraint->evaluate($invocation->getMethodName(), '', true); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Invocation matcher which checks if a method was invoked at a certain index. - * - * If the expected index number does not match the current invocation index it - * will not match which means it skips all method and parameter matching. Only - * once the index is reached will the method and parameter start matching and - * verifying. - * - * If the index is never reached it will throw an exception in index. - */ -class InvokedAtIndex implements Invocation -{ - /** - * @var int - */ - private $sequenceIndex; - - /** - * @var int - */ - private $currentIndex = -1; - - /** - * @param int $sequenceIndex - */ - public function __construct($sequenceIndex) - { - $this->sequenceIndex = $sequenceIndex; - } - - public function toString(): string - { - return 'invoked at sequence index ' . $this->sequenceIndex; - } - - /** - * @return bool - */ - public function matches(BaseInvocation $invocation) - { - $this->currentIndex++; - - return $this->currentIndex == $this->sequenceIndex; - } - - public function invoked(BaseInvocation $invocation): void - { - } - - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function verify(): void - { - if ($this->currentIndex < $this->sequenceIndex) { - throw new ExpectationFailedException( - \sprintf( - 'The expected invocation at index %s was never reached.', - $this->sequenceIndex - ) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Invocation matcher which checks if a method has been invoked a certain amount - * of times. - * If the number of invocations exceeds the value it will immediately throw an - * exception, - * If the number is less it will later be checked in verify() and also throw an - * exception. - */ -class InvokedCount extends InvokedRecorder -{ - /** - * @var int - */ - private $expectedCount; - - /** - * @param int $expectedCount - */ - public function __construct($expectedCount) - { - $this->expectedCount = $expectedCount; - } - - /** - * @return bool - */ - public function isNever() - { - return $this->expectedCount === 0; - } - - public function toString(): string - { - return 'invoked ' . $this->expectedCount . ' time(s)'; - } - - /** - * @throws ExpectationFailedException - */ - public function invoked(BaseInvocation $invocation): void - { - parent::invoked($invocation); - - $count = $this->getInvocationCount(); - - if ($count > $this->expectedCount) { - $message = $invocation->toString() . ' '; - - switch ($this->expectedCount) { - case 0: - $message .= 'was not expected to be called.'; - - break; - - case 1: - $message .= 'was not expected to be called more than once.'; - - break; - - default: - $message .= \sprintf( - 'was not expected to be called more than %d times.', - $this->expectedCount - ); - } - - throw new ExpectationFailedException($message); - } - } - - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function verify(): void - { - $count = $this->getInvocationCount(); - - if ($count !== $this->expectedCount) { - throw new ExpectationFailedException( - \sprintf( - 'Method was expected to be called %d times, ' . - 'actually called %d times.', - $this->expectedCount, - $count - ) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Records invocations and provides convenience methods for checking them later - * on. - * This abstract class can be implemented by matchers which needs to check the - * number of times an invocation has occurred. - */ -abstract class InvokedRecorder implements Invocation -{ - /** - * @var BaseInvocation[] - */ - private $invocations = []; - - /** - * @return int - */ - public function getInvocationCount() - { - return \count($this->invocations); - } - - /** - * @return BaseInvocation[] - */ - public function getInvocations() - { - return $this->invocations; - } - - /** - * @return bool - */ - public function hasBeenInvoked() - { - return \count($this->invocations) > 0; - } - - public function invoked(BaseInvocation $invocation): void - { - $this->invocations[] = $invocation; - } - - /** - * @return bool - */ - public function matches(BaseInvocation $invocation) - { - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -/** - * Invocation matcher which looks for sets of specific parameters in the invocations. - * - * Checks the parameters of the incoming invocations, the parameter list is - * checked against the defined constraints in $parameters. If the constraint - * is met it will return true in matches(). - * - * It takes a list of match groups and and increases a call index after each invocation. - * So the first invocation uses the first group of constraints, the second the next and so on. - */ -class ConsecutiveParameters extends StatelessInvocation -{ - /** - * @var array - */ - private $parameterGroups = []; - - /** - * @var array - */ - private $invocations = []; - - /** - * @throws \PHPUnit\Framework\Exception - */ - public function __construct(array $parameterGroups) - { - foreach ($parameterGroups as $index => $parameters) { - foreach ($parameters as $parameter) { - if (!$parameter instanceof Constraint) { - $parameter = new IsEqual($parameter); - } - - $this->parameterGroups[$index][] = $parameter; - } - } - } - - public function toString(): string - { - return 'with consecutive parameters'; - } - - /** - * @throws \PHPUnit\Framework\ExpectationFailedException - * - * @return bool - */ - public function matches(BaseInvocation $invocation) - { - $this->invocations[] = $invocation; - $callIndex = \count($this->invocations) - 1; - - $this->verifyInvocation($invocation, $callIndex); - - return false; - } - - public function verify(): void - { - foreach ($this->invocations as $callIndex => $invocation) { - $this->verifyInvocation($invocation, $callIndex); - } - } - - /** - * Verify a single invocation - * - * @param int $callIndex - * - * @throws ExpectationFailedException - */ - private function verifyInvocation(BaseInvocation $invocation, $callIndex): void - { - if (!isset($this->parameterGroups[$callIndex])) { - // no parameter assertion for this call index - return; - } - - if ($invocation === null) { - throw new ExpectationFailedException( - 'Mocked method does not exist.' - ); - } - - $parameters = $this->parameterGroups[$callIndex]; - - if (\count($invocation->getParameters()) < \count($parameters)) { - throw new ExpectationFailedException( - \sprintf( - 'Parameter count for invocation %s is too low.', - $invocation->toString() - ) - ); - } - - foreach ($parameters as $i => $parameter) { - $parameter->evaluate( - $invocation->getParameters()[$i], - \sprintf( - 'Parameter %s for invocation #%d %s does not match expected ' . - 'value.', - $i, - $callIndex, - $invocation->toString() - ) - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -/** - * Invocation matcher which checks if a method has been invoked zero or more - * times. This matcher will always match. - */ -class AnyInvokedCount extends InvokedRecorder -{ - public function toString(): string - { - return 'invoked zero or more times'; - } - - public function verify(): void - { - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; -use PHPUnit\Framework\MockObject\Verifiable; -use PHPUnit\Framework\SelfDescribing; - -/** - * Interface for classes which matches an invocation based on its - * method name, argument, order or call count. - */ -interface Invocation extends SelfDescribing, Verifiable -{ - /** - * Registers the invocation $invocation in the object as being invoked. - * This will only occur after matches() returns true which means the - * current invocation is the correct one. - * - * The matcher can store information from the invocation which can later - * be checked in verify(), or it can check the values directly and throw - * and exception if an expectation is not met. - * - * If the matcher is a stub it will also have a return value. - * - * @param BaseInvocation $invocation Object containing information on a mocked or stubbed method which was invoked - */ - public function invoked(BaseInvocation $invocation); - - /** - * Checks if the invocation $invocation matches the current rules. If it does - * the matcher will get the invoked() method called which should check if an - * expectation is met. - * - * @param BaseInvocation $invocation Object containing information on a mocked or stubbed method which was invoked - * - * @return bool - */ - public function matches(BaseInvocation $invocation); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Invocation matcher which checks if a method has been invoked at least - * N times. - */ -class InvokedAtLeastCount extends InvokedRecorder -{ - /** - * @var int - */ - private $requiredInvocations; - - /** - * @param int $requiredInvocations - */ - public function __construct($requiredInvocations) - { - $this->requiredInvocations = $requiredInvocations; - } - - public function toString(): string - { - return 'invoked at least ' . $this->requiredInvocations . ' times'; - } - - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function verify(): void - { - $count = $this->getInvocationCount(); - - if ($count < $this->requiredInvocations) { - throw new ExpectationFailedException( - 'Expected invocation at least ' . $this->requiredInvocations . - ' times but it occurred ' . $count . ' time(s).' - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\MockObject\Invocation as BaseInvocation; - -class DeferredError extends StatelessInvocation -{ - /** - * @var \Throwable - */ - private $exception; - - public function __construct(\Throwable $exception) - { - $this->exception = $exception; - } - - public function verify(): void - { - throw $this->exception; - } - - public function toString(): string - { - return ''; - } - - public function matches(BaseInvocation $invocation): bool - { - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Matcher; - -use PHPUnit\Framework\ExpectationFailedException; - -/** - * Invocation matcher which checks if a method has been invoked at least - * N times. - */ -class InvokedAtMostCount extends InvokedRecorder -{ - /** - * @var int - */ - private $allowedInvocations; - - /** - * @param int $allowedInvocations - */ - public function __construct($allowedInvocations) - { - $this->allowedInvocations = $allowedInvocations; - } - - public function toString(): string - { - return 'invoked at most ' . $this->allowedInvocations . ' times'; - } - - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function verify(): void - { - $count = $this->getInvocationCount(); - - if ($count > $this->allowedInvocations) { - throw new ExpectationFailedException( - 'Expected invocation at most ' . $this->allowedInvocations . - ' times but it occurred ' . $count . ' time(s).' - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\TestCase; - -/** - * Implementation of the Builder pattern for Mock objects. - */ -class MockBuilder -{ - /** - * @var TestCase - */ - private $testCase; - - /** - * @var string - */ - private $type; - - /** - * @var array - */ - private $methods = []; - - /** - * @var array - */ - private $methodsExcept = []; - - /** - * @var string - */ - private $mockClassName = ''; - - /** - * @var array - */ - private $constructorArgs = []; - - /** - * @var bool - */ - private $originalConstructor = true; - - /** - * @var bool - */ - private $originalClone = true; - - /** - * @var bool - */ - private $autoload = true; - - /** - * @var bool - */ - private $cloneArguments = false; - - /** - * @var bool - */ - private $callOriginalMethods = false; - - /** - * @var object - */ - private $proxyTarget; - - /** - * @var bool - */ - private $allowMockingUnknownTypes = true; - - /** - * @var bool - */ - private $returnValueGeneration = true; - - /** - * @var Generator - */ - private $generator; - - /** - * @param array|string $type - */ - public function __construct(TestCase $testCase, $type) - { - $this->testCase = $testCase; - $this->type = $type; - $this->generator = new Generator; - } - - /** - * Creates a mock object using a fluent interface. - * - * @return MockObject - */ - public function getMock() - { - $object = $this->generator->getMock( - $this->type, - $this->methods, - $this->constructorArgs, - $this->mockClassName, - $this->originalConstructor, - $this->originalClone, - $this->autoload, - $this->cloneArguments, - $this->callOriginalMethods, - $this->proxyTarget, - $this->allowMockingUnknownTypes, - $this->returnValueGeneration - ); - - $this->testCase->registerMockObject($object); - - return $object; - } - - /** - * Creates a mock object for an abstract class using a fluent interface. - * - * @return MockObject - */ - public function getMockForAbstractClass() - { - $object = $this->generator->getMockForAbstractClass( - $this->type, - $this->constructorArgs, - $this->mockClassName, - $this->originalConstructor, - $this->originalClone, - $this->autoload, - $this->methods, - $this->cloneArguments - ); - - $this->testCase->registerMockObject($object); - - return $object; - } - - /** - * Creates a mock object for a trait using a fluent interface. - * - * @return MockObject - */ - public function getMockForTrait() - { - $object = $this->generator->getMockForTrait( - $this->type, - $this->constructorArgs, - $this->mockClassName, - $this->originalConstructor, - $this->originalClone, - $this->autoload, - $this->methods, - $this->cloneArguments - ); - - $this->testCase->registerMockObject($object); - - return $object; - } - - /** - * Specifies the subset of methods to mock. Default is to mock none of them. - * - * @return MockBuilder - */ - public function setMethods(array $methods = null) - { - $this->methods = $methods; - - return $this; - } - - /** - * Specifies the subset of methods to not mock. Default is to mock all of them. - * - * @return MockBuilder - */ - public function setMethodsExcept(array $methods = []) - { - $this->methodsExcept = $methods; - - $this->setMethods( - \array_diff( - $this->generator->getClassMethods($this->type), - $this->methodsExcept - ) - ); - - return $this; - } - - /** - * Specifies the arguments for the constructor. - * - * @return MockBuilder - */ - public function setConstructorArgs(array $args) - { - $this->constructorArgs = $args; - - return $this; - } - - /** - * Specifies the name for the mock class. - * - * @param string $name - * - * @return MockBuilder - */ - public function setMockClassName($name) - { - $this->mockClassName = $name; - - return $this; - } - - /** - * Disables the invocation of the original constructor. - * - * @return MockBuilder - */ - public function disableOriginalConstructor() - { - $this->originalConstructor = false; - - return $this; - } - - /** - * Enables the invocation of the original constructor. - * - * @return MockBuilder - */ - public function enableOriginalConstructor() - { - $this->originalConstructor = true; - - return $this; - } - - /** - * Disables the invocation of the original clone constructor. - * - * @return MockBuilder - */ - public function disableOriginalClone() - { - $this->originalClone = false; - - return $this; - } - - /** - * Enables the invocation of the original clone constructor. - * - * @return MockBuilder - */ - public function enableOriginalClone() - { - $this->originalClone = true; - - return $this; - } - - /** - * Disables the use of class autoloading while creating the mock object. - * - * @return MockBuilder - */ - public function disableAutoload() - { - $this->autoload = false; - - return $this; - } - - /** - * Enables the use of class autoloading while creating the mock object. - * - * @return MockBuilder - */ - public function enableAutoload() - { - $this->autoload = true; - - return $this; - } - - /** - * Disables the cloning of arguments passed to mocked methods. - * - * @return MockBuilder - */ - public function disableArgumentCloning() - { - $this->cloneArguments = false; - - return $this; - } - - /** - * Enables the cloning of arguments passed to mocked methods. - * - * @return MockBuilder - */ - public function enableArgumentCloning() - { - $this->cloneArguments = true; - - return $this; - } - - /** - * Enables the invocation of the original methods. - * - * @return MockBuilder - */ - public function enableProxyingToOriginalMethods() - { - $this->callOriginalMethods = true; - - return $this; - } - - /** - * Disables the invocation of the original methods. - * - * @return MockBuilder - */ - public function disableProxyingToOriginalMethods() - { - $this->callOriginalMethods = false; - $this->proxyTarget = null; - - return $this; - } - - /** - * Sets the proxy target. - * - * @param object $object - * - * @return MockBuilder - */ - public function setProxyTarget($object) - { - $this->proxyTarget = $object; - - return $this; - } - - /** - * @return MockBuilder - */ - public function allowMockingUnknownTypes() - { - $this->allowMockingUnknownTypes = true; - - return $this; - } - - /** - * @return MockBuilder - */ - public function disallowMockingUnknownTypes() - { - $this->allowMockingUnknownTypes = false; - - return $this; - } - - /** - * @return MockBuilder - */ - public function enableAutoReturnValueGeneration() - { - $this->returnValueGeneration = true; - - return $this; - } - - /** - * @return MockBuilder - */ - public function disableAutoReturnValueGeneration() - { - $this->returnValueGeneration = false; - - return $this; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; - -/** - * Stubs a method by returning an argument that was passed to the mocked method. - */ -class ReturnArgument implements Stub -{ - /** - * @var int - */ - private $argumentIndex; - - public function __construct($argumentIndex) - { - $this->argumentIndex = $argumentIndex; - } - - public function invoke(Invocation $invocation) - { - if (isset($invocation->getParameters()[$this->argumentIndex])) { - return $invocation->getParameters()[$this->argumentIndex]; - } - } - - public function toString(): string - { - return \sprintf('return argument #%d', $this->argumentIndex); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; -use SebastianBergmann\Exporter\Exporter; - -/** - * Stubs a method by returning a user-defined stack of values. - */ -class ConsecutiveCalls implements Stub -{ - /** - * @var array - */ - private $stack; - - /** - * @var mixed - */ - private $value; - - public function __construct(array $stack) - { - $this->stack = $stack; - } - - public function invoke(Invocation $invocation) - { - $this->value = \array_shift($this->stack); - - if ($this->value instanceof Stub) { - $this->value = $this->value->invoke($invocation); - } - - return $this->value; - } - - public function toString(): string - { - $exporter = new Exporter; - - return \sprintf( - 'return user-specified value %s', - $exporter->export($this->value) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Matcher\Invocation; - -/** - * Stubs a method by returning a user-defined value. - */ -interface MatcherCollection -{ - /** - * Adds a new matcher to the collection which can be used as an expectation - * or a stub. - * - * @param Invocation $matcher Matcher for invocations to mock objects - */ - public function addMatcher(Invocation $matcher); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; -use SebastianBergmann\Exporter\Exporter; - -/** - * Stubs a method by raising a user-defined exception. - */ -class Exception implements Stub -{ - private $exception; - - public function __construct(\Throwable $exception) - { - $this->exception = $exception; - } - - public function invoke(Invocation $invocation): void - { - throw $this->exception; - } - - public function toString(): string - { - $exporter = new Exporter; - - return \sprintf( - 'raise user-specified exception %s', - $exporter->export($this->exception) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; - -/** - * Stubs a method by returning a value from a map. - */ -class ReturnValueMap implements Stub -{ - /** - * @var array - */ - private $valueMap; - - public function __construct(array $valueMap) - { - $this->valueMap = $valueMap; - } - - public function invoke(Invocation $invocation) - { - $parameterCount = \count($invocation->getParameters()); - - foreach ($this->valueMap as $map) { - if (!\is_array($map) || $parameterCount !== (\count($map) - 1)) { - continue; - } - - $return = \array_pop($map); - - if ($invocation->getParameters() === $map) { - return $return; - } - } - } - - public function toString(): string - { - return 'return value from a map'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; -use SebastianBergmann\Exporter\Exporter; - -/** - * Stubs a method by returning a user-defined reference to a value. - */ -class ReturnReference implements Stub -{ - /** - * @var mixed - */ - private $reference; - - public function __construct(&$reference) - { - $this->reference = &$reference; - } - - public function invoke(Invocation $invocation) - { - return $this->reference; - } - - public function toString(): string - { - $exporter = new Exporter; - - return \sprintf( - 'return user-specified reference %s', - $exporter->export($this->reference) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; -use SebastianBergmann\Exporter\Exporter; - -/** - * Stubs a method by returning a user-defined value. - */ -class ReturnStub implements Stub -{ - /** - * @var mixed - */ - private $value; - - public function __construct($value) - { - $this->value = $value; - } - - public function invoke(Invocation $invocation) - { - return $this->value; - } - - public function toString(): string - { - $exporter = new Exporter; - - return \sprintf( - 'return user-specified value %s', - $exporter->export($this->value) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Stub; - -class ReturnCallback implements Stub -{ - private $callback; - - public function __construct($callback) - { - $this->callback = $callback; - } - - public function invoke(Invocation $invocation) - { - return \call_user_func_array($this->callback, $invocation->getParameters()); - } - - public function toString(): string - { - if (\is_array($this->callback)) { - if (\is_object($this->callback[0])) { - $class = \get_class($this->callback[0]); - $type = '->'; - } else { - $class = $this->callback[0]; - $type = '::'; - } - - return \sprintf( - 'return result of user defined callback %s%s%s() with the ' . - 'passed arguments', - $class, - $type, - $this->callback[1] - ); - } - - return 'return result of user defined callback ' . $this->callback . - ' with the passed arguments'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Stub; - -use PHPUnit\Framework\MockObject\Invocation; -use PHPUnit\Framework\MockObject\Invocation\ObjectInvocation; -use PHPUnit\Framework\MockObject\RuntimeException; -use PHPUnit\Framework\MockObject\Stub; - -/** - * Stubs a method by returning the current object. - */ -class ReturnSelf implements Stub -{ - public function invoke(Invocation $invocation) - { - if (!$invocation instanceof ObjectInvocation) { - throw new RuntimeException( - 'The current object can only be returned when mocking an ' . - 'object, not a static class.' - ); - } - - return $invocation->getObject(); - } - - public function toString(): string - { - return 'return the current object'; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -final class MockMethodSet -{ - /** - * @var MockMethod[] - */ - private $methods = []; - - public function addMethods(MockMethod ...$methods): void - { - foreach ($methods as $method) { - $this->methods[\strtolower($method->getName())] = $method; - } - } - - public function asArray(): array - { - return \array_values($this->methods); - } - - public function hasMethod(string $methodName): bool - { - return \array_key_exists(\strtolower($methodName), $this->methods); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount; -use PHPUnit\Framework\MockObject\Matcher\AnyParameters; -use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation; -use PHPUnit\Framework\MockObject\Matcher\InvokedCount; -use PHPUnit\Framework\MockObject\Matcher\MethodName; -use PHPUnit\Framework\MockObject\Matcher\Parameters; -use PHPUnit\Framework\TestFailure; - -/** - * Main matcher which defines a full expectation using method, parameter and - * invocation matchers. - * This matcher encapsulates all the other matchers and allows the builder to - * set the specific matchers when the appropriate methods are called (once(), - * where() etc.). - * - * All properties are public so that they can easily be accessed by the builder. - */ -class Matcher implements MatcherInvocation -{ - /** - * @var MatcherInvocation - */ - private $invocationMatcher; - - /** - * @var mixed - */ - private $afterMatchBuilderId; - - /** - * @var bool - */ - private $afterMatchBuilderIsInvoked = false; - - /** - * @var MethodName - */ - private $methodNameMatcher; - - /** - * @var Parameters - */ - private $parametersMatcher; - - /** - * @var Stub - */ - private $stub; - - public function __construct(MatcherInvocation $invocationMatcher) - { - $this->invocationMatcher = $invocationMatcher; - } - - public function hasMatchers(): bool - { - return $this->invocationMatcher !== null && !$this->invocationMatcher instanceof AnyInvokedCount; - } - - public function hasMethodNameMatcher(): bool - { - return $this->methodNameMatcher !== null; - } - - public function getMethodNameMatcher(): MethodName - { - return $this->methodNameMatcher; - } - - public function setMethodNameMatcher(MethodName $matcher): void - { - $this->methodNameMatcher = $matcher; - } - - public function hasParametersMatcher(): bool - { - return $this->parametersMatcher !== null; - } - - public function getParametersMatcher(): Parameters - { - return $this->parametersMatcher; - } - - public function setParametersMatcher($matcher): void - { - $this->parametersMatcher = $matcher; - } - - public function setStub($stub): void - { - $this->stub = $stub; - } - - public function setAfterMatchBuilderId($id): void - { - $this->afterMatchBuilderId = $id; - } - - /** - * @throws \Exception - * @throws RuntimeException - * @throws ExpectationFailedException - */ - public function invoked(Invocation $invocation) - { - if ($this->invocationMatcher === null) { - throw new RuntimeException( - 'No invocation matcher is set' - ); - } - - if ($this->methodNameMatcher === null) { - throw new RuntimeException('No method matcher is set'); - } - - if ($this->afterMatchBuilderId !== null) { - $builder = $invocation->getObject() - ->__phpunit_getInvocationMocker() - ->lookupId($this->afterMatchBuilderId); - - if (!$builder) { - throw new RuntimeException( - \sprintf( - 'No builder found for match builder identification <%s>', - $this->afterMatchBuilderId - ) - ); - } - - $matcher = $builder->getMatcher(); - - if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) { - $this->afterMatchBuilderIsInvoked = true; - } - } - - $this->invocationMatcher->invoked($invocation); - - try { - if ($this->parametersMatcher !== null && - !$this->parametersMatcher->matches($invocation)) { - $this->parametersMatcher->verify(); - } - } catch (ExpectationFailedException $e) { - throw new ExpectationFailedException( - \sprintf( - "Expectation failed for %s when %s\n%s", - $this->methodNameMatcher->toString(), - $this->invocationMatcher->toString(), - $e->getMessage() - ), - $e->getComparisonFailure() - ); - } - - if ($this->stub) { - return $this->stub->invoke($invocation); - } - - return $invocation->generateReturnValue(); - } - - /** - * @throws RuntimeException - * @throws ExpectationFailedException - * - * @return bool - */ - public function matches(Invocation $invocation) - { - if ($this->afterMatchBuilderId !== null) { - $builder = $invocation->getObject() - ->__phpunit_getInvocationMocker() - ->lookupId($this->afterMatchBuilderId); - - if (!$builder) { - throw new RuntimeException( - \sprintf( - 'No builder found for match builder identification <%s>', - $this->afterMatchBuilderId - ) - ); - } - - $matcher = $builder->getMatcher(); - - if (!$matcher) { - return false; - } - - if (!$matcher->invocationMatcher->hasBeenInvoked()) { - return false; - } - } - - if ($this->invocationMatcher === null) { - throw new RuntimeException( - 'No invocation matcher is set' - ); - } - - if ($this->methodNameMatcher === null) { - throw new RuntimeException('No method matcher is set'); - } - - if (!$this->invocationMatcher->matches($invocation)) { - return false; - } - - try { - if (!$this->methodNameMatcher->matches($invocation)) { - return false; - } - } catch (ExpectationFailedException $e) { - throw new ExpectationFailedException( - \sprintf( - "Expectation failed for %s when %s\n%s", - $this->methodNameMatcher->toString(), - $this->invocationMatcher->toString(), - $e->getMessage() - ), - $e->getComparisonFailure() - ); - } - - return true; - } - - /** - * @throws RuntimeException - * @throws ExpectationFailedException - */ - public function verify(): void - { - if ($this->invocationMatcher === null) { - throw new RuntimeException( - 'No invocation matcher is set' - ); - } - - if ($this->methodNameMatcher === null) { - throw new RuntimeException('No method matcher is set'); - } - - try { - $this->invocationMatcher->verify(); - - if ($this->parametersMatcher === null) { - $this->parametersMatcher = new AnyParameters; - } - - $invocationIsAny = $this->invocationMatcher instanceof AnyInvokedCount; - $invocationIsNever = $this->invocationMatcher instanceof InvokedCount && $this->invocationMatcher->isNever(); - - if (!$invocationIsAny && !$invocationIsNever) { - $this->parametersMatcher->verify(); - } - } catch (ExpectationFailedException $e) { - throw new ExpectationFailedException( - \sprintf( - "Expectation failed for %s when %s.\n%s", - $this->methodNameMatcher->toString(), - $this->invocationMatcher->toString(), - TestFailure::exceptionToString($e) - ) - ); - } - } - - public function toString(): string - { - $list = []; - - if ($this->invocationMatcher !== null) { - $list[] = $this->invocationMatcher->toString(); - } - - if ($this->methodNameMatcher !== null) { - $list[] = 'where ' . $this->methodNameMatcher->toString(); - } - - if ($this->parametersMatcher !== null) { - $list[] = 'and ' . $this->parametersMatcher->toString(); - } - - if ($this->afterMatchBuilderId !== null) { - $list[] = 'after ' . $this->afterMatchBuilderId; - } - - if ($this->stub !== null) { - $list[] = 'will ' . $this->stub->toString(); - } - - return \implode(' ', $list); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\MockObject\Matcher; -use PHPUnit\Framework\MockObject\Matcher\Invocation; -use PHPUnit\Framework\MockObject\RuntimeException; -use PHPUnit\Framework\MockObject\Stub; -use PHPUnit\Framework\MockObject\Stub\MatcherCollection; - -/** - * Builder for mocked or stubbed invocations. - * - * Provides methods for building expectations without having to resort to - * instantiating the various matchers manually. These methods also form a - * more natural way of reading the expectation. This class should be together - * with the test case PHPUnit\Framework\MockObject\TestCase. - */ -class InvocationMocker implements MethodNameMatch -{ - /** - * @var MatcherCollection - */ - private $collection; - - /** - * @var Matcher - */ - private $matcher; - - /** - * @var string[] - */ - private $configurableMethods; - - public function __construct(MatcherCollection $collection, Invocation $invocationMatcher, array $configurableMethods) - { - $this->collection = $collection; - $this->matcher = new Matcher($invocationMatcher); - - $this->collection->addMatcher($this->matcher); - - $this->configurableMethods = $configurableMethods; - } - - /** - * @return Matcher - */ - public function getMatcher() - { - return $this->matcher; - } - - /** - * @return InvocationMocker - */ - public function id($id) - { - $this->collection->registerId($id, $this); - - return $this; - } - - /** - * @return InvocationMocker - */ - public function will(Stub $stub) - { - $this->matcher->setStub($stub); - - return $this; - } - - /** - * @return InvocationMocker - */ - public function willReturn($value, ...$nextValues) - { - if (\count($nextValues) === 0) { - $stub = new Stub\ReturnStub($value); - } else { - $stub = new Stub\ConsecutiveCalls( - \array_merge([$value], $nextValues) - ); - } - - return $this->will($stub); - } - - /** - * @param mixed $reference - * - * @return InvocationMocker - */ - public function willReturnReference(&$reference) - { - $stub = new Stub\ReturnReference($reference); - - return $this->will($stub); - } - - /** - * @return InvocationMocker - */ - public function willReturnMap(array $valueMap) - { - $stub = new Stub\ReturnValueMap($valueMap); - - return $this->will($stub); - } - - /** - * @return InvocationMocker - */ - public function willReturnArgument($argumentIndex) - { - $stub = new Stub\ReturnArgument($argumentIndex); - - return $this->will($stub); - } - - /** - * @param callable $callback - * - * @return InvocationMocker - */ - public function willReturnCallback($callback) - { - $stub = new Stub\ReturnCallback($callback); - - return $this->will($stub); - } - - /** - * @return InvocationMocker - */ - public function willReturnSelf() - { - $stub = new Stub\ReturnSelf; - - return $this->will($stub); - } - - /** - * @return InvocationMocker - */ - public function willReturnOnConsecutiveCalls(...$values) - { - $stub = new Stub\ConsecutiveCalls($values); - - return $this->will($stub); - } - - /** - * @return InvocationMocker - */ - public function willThrowException(\Exception $exception) - { - $stub = new Stub\Exception($exception); - - return $this->will($stub); - } - - /** - * @return InvocationMocker - */ - public function after($id) - { - $this->matcher->setAfterMatchBuilderId($id); - - return $this; - } - - /** - * @param array ...$arguments - * - * @throws RuntimeException - * - * @return InvocationMocker - */ - public function with(...$arguments) - { - $this->canDefineParameters(); - - $this->matcher->setParametersMatcher(new Matcher\Parameters($arguments)); - - return $this; - } - - /** - * @param array ...$arguments - * - * @throws RuntimeException - * - * @return InvocationMocker - */ - public function withConsecutive(...$arguments) - { - $this->canDefineParameters(); - - $this->matcher->setParametersMatcher(new Matcher\ConsecutiveParameters($arguments)); - - return $this; - } - - /** - * @throws RuntimeException - * - * @return InvocationMocker - */ - public function withAnyParameters() - { - $this->canDefineParameters(); - - $this->matcher->setParametersMatcher(new Matcher\AnyParameters); - - return $this; - } - - /** - * @param Constraint|string $constraint - * - * @throws RuntimeException - * - * @return InvocationMocker - */ - public function method($constraint) - { - if ($this->matcher->hasMethodNameMatcher()) { - throw new RuntimeException( - 'Method name matcher is already defined, cannot redefine' - ); - } - - if (\is_string($constraint) && !\in_array(\strtolower($constraint), $this->configurableMethods, true)) { - throw new RuntimeException( - \sprintf( - 'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', - $constraint - ) - ); - } - - $this->matcher->setMethodNameMatcher(new Matcher\MethodName($constraint)); - - return $this; - } - - /** - * Validate that a parameters matcher can be defined, throw exceptions otherwise. - * - * @throws RuntimeException - */ - private function canDefineParameters(): void - { - if (!$this->matcher->hasMethodNameMatcher()) { - throw new RuntimeException( - 'Method name matcher is not defined, cannot define parameter ' . - 'matcher without one' - ); - } - - if ($this->matcher->hasParametersMatcher()) { - throw new RuntimeException( - 'Parameter matcher is already defined, cannot redefine' - ); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\MockObject\Stub as BaseStub; - -/** - * Builder interface for stubs which are actions replacing an invocation. - */ -interface Stub extends Identity -{ - /** - * Stubs the matching method with the stub object $stub. Any invocations of - * the matched method will now be handled by the stub instead. - * - * @return Identity - */ - public function will(BaseStub $stub); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * Interface for builders which can register builders with a given identification. - * - * This interface relates to Identity. - */ -interface NamespaceMatch -{ - /** - * Looks up the match builder with identification $id and returns it. - * - * @param string $id The identification of the match builder - * - * @return Match - */ - public function lookupId($id); - - /** - * Registers the match builder $builder with the identification $id. The - * builder can later be looked up using lookupId() to figure out if it - * has been invoked. - * - * @param string $id The identification of the match builder - * @param Match $builder The builder which is being registered - */ - public function registerId($id, Match $builder); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * Builder interface for unique identifiers. - * - * Defines the interface for recording unique identifiers. The identifiers - * can be used to define the invocation order of expectations. The expectation - * is recorded using id() and then defined in order using - * PHPUnit\Framework\MockObject\Builder\Match::after(). - */ -interface Identity -{ - /** - * Sets the identification of the expectation to $id. - * - * @note The identifier is unique per mock object. - * - * @param string $id unique identification of expectation - */ - public function id($id); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -use PHPUnit\Framework\MockObject\Matcher\AnyParameters; - -/** - * Builder interface for parameter matchers. - */ -interface ParametersMatch extends Match -{ - /** - * Sets the parameters to match for, each parameter to this function will - * be part of match. To perform specific matches or constraints create a - * new PHPUnit\Framework\Constraint\Constraint and use it for the parameter. - * If the parameter value is not a constraint it will use the - * PHPUnit\Framework\Constraint\IsEqual for the value. - * - * Some examples: - * - * // match first parameter with value 2 - * $b->with(2); - * // match first parameter with value 'smock' and second identical to 42 - * $b->with('smock', new PHPUnit\Framework\Constraint\IsEqual(42)); - * - * - * @return ParametersMatch - */ - public function with(...$arguments); - - /** - * Sets a matcher which allows any kind of parameters. - * - * Some examples: - * - * // match any number of parameters - * $b->withAnyParameters(); - * - * - * @return AnyParameters - */ - public function withAnyParameters(); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * Builder interface for invocation order matches. - */ -interface Match extends Stub -{ - /** - * Defines the expectation which must occur before the current is valid. - * - * @param string $id the identification of the expectation that should - * occur before this one - * - * @return Stub - */ - public function after($id); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject\Builder; - -/** - * Builder interface for matcher of method names. - */ -interface MethodNameMatch extends ParametersMatch -{ - /** - * Adds a new method name match and returns the parameter match object for - * further matching possibilities. - * - * @param \PHPUnit\Framework\Constraint\Constraint $name Constraint for matching method, if a string is passed it will use the PHPUnit_Framework_Constraint_IsEqual - * - * @return ParametersMatch - */ - public function method($name); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework\MockObject; - -use Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException; -use Doctrine\Instantiator\Instantiator; -use Iterator; -use IteratorAggregate; -use PHPUnit\Framework\Exception; -use PHPUnit\Util\InvalidArgumentHelper; -use ReflectionClass; -use ReflectionMethod; -use SoapClient; -use Text_Template; -use Traversable; - -/** - * Mock Object Code Generator - */ -class Generator -{ - /** - * @var array - */ - private const BLACKLISTED_METHOD_NAMES = [ - '__CLASS__' => true, - '__DIR__' => true, - '__FILE__' => true, - '__FUNCTION__' => true, - '__LINE__' => true, - '__METHOD__' => true, - '__NAMESPACE__' => true, - '__TRAIT__' => true, - '__clone' => true, - '__halt_compiler' => true, - ]; - - /** - * @var array - */ - private static $cache = []; - - /** - * @var Text_Template[] - */ - private static $templates = []; - - /** - * Returns a mock object for the specified class. - * - * @param string|string[] $type - * @param array $methods - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param bool $cloneArguments - * @param bool $callOriginalMethods - * @param object $proxyTarget - * @param bool $allowMockingUnknownTypes - * @param bool $returnValueGeneration - * - * @throws Exception - * @throws RuntimeException - * @throws \PHPUnit\Framework\Exception - * @throws \ReflectionException - * - * @return MockObject - */ - public function getMock($type, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null, $allowMockingUnknownTypes = true, $returnValueGeneration = true) - { - if (!\is_array($type) && !\is_string($type)) { - throw InvalidArgumentHelper::factory(1, 'array or string'); - } - - if (!\is_string($mockClassName)) { - throw InvalidArgumentHelper::factory(4, 'string'); - } - - if (!\is_array($methods) && null !== $methods) { - throw InvalidArgumentHelper::factory(2, 'array', $methods); - } - - if ($type === 'Traversable' || $type === '\\Traversable') { - $type = 'Iterator'; - } - - if (\is_array($type)) { - $type = \array_unique( - \array_map( - function ($type) { - if ($type === 'Traversable' || - $type === '\\Traversable' || - $type === '\\Iterator') { - return 'Iterator'; - } - - return $type; - }, - $type - ) - ); - } - - if (!$allowMockingUnknownTypes) { - if (\is_array($type)) { - foreach ($type as $_type) { - if (!\class_exists($_type, $callAutoload) && - !\interface_exists($_type, $callAutoload)) { - throw new RuntimeException( - \sprintf( - 'Cannot stub or mock class or interface "%s" which does not exist', - $_type - ) - ); - } - } - } else { - if (!\class_exists($type, $callAutoload) && - !\interface_exists($type, $callAutoload) - ) { - throw new RuntimeException( - \sprintf( - 'Cannot stub or mock class or interface "%s" which does not exist', - $type - ) - ); - } - } - } - - if (null !== $methods) { - foreach ($methods as $method) { - if (!\preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) { - throw new RuntimeException( - \sprintf( - 'Cannot stub or mock method with invalid name "%s"', - $method - ) - ); - } - } - - if ($methods !== \array_unique($methods)) { - throw new RuntimeException( - \sprintf( - 'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")', - \implode(', ', $methods), - \implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods)))) - ) - ); - } - } - - if ($mockClassName !== '' && \class_exists($mockClassName, false)) { - $reflect = new ReflectionClass($mockClassName); - - if (!$reflect->implementsInterface(MockObject::class)) { - throw new RuntimeException( - \sprintf( - 'Class "%s" already exists.', - $mockClassName - ) - ); - } - } - - if ($callOriginalConstructor === false && $callOriginalMethods === true) { - throw new RuntimeException( - 'Proxying to original methods requires invoking the original constructor' - ); - } - - $mock = $this->generate( - $type, - $methods, - $mockClassName, - $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods - ); - - return $this->getObject( - $mock['code'], - $mock['mockClassName'], - $type, - $callOriginalConstructor, - $callAutoload, - $arguments, - $callOriginalMethods, - $proxyTarget, - $returnValueGeneration - ); - } - - /** - * Returns a mock object for the specified abstract class with all abstract - * methods of the class mocked. Concrete methods to mock can be specified with - * the last parameter - * - * @param string $originalClassName - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param array $mockedMethods - * @param bool $cloneArguments - * - * @throws \ReflectionException - * @throws RuntimeException - * @throws Exception - * - * @return MockObject - */ - public function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true) - { - if (!\is_string($originalClassName)) { - throw InvalidArgumentHelper::factory(1, 'string'); - } - - if (!\is_string($mockClassName)) { - throw InvalidArgumentHelper::factory(3, 'string'); - } - - if (\class_exists($originalClassName, $callAutoload) || - \interface_exists($originalClassName, $callAutoload)) { - $reflector = new ReflectionClass($originalClassName); - $methods = $mockedMethods; - - foreach ($reflector->getMethods() as $method) { - if ($method->isAbstract() && !\in_array($method->getName(), $methods, true)) { - $methods[] = $method->getName(); - } - } - - if (empty($methods)) { - $methods = null; - } - - return $this->getMock( - $originalClassName, - $methods, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $cloneArguments - ); - } - - throw new RuntimeException( - \sprintf('Class "%s" does not exist.', $originalClassName) - ); - } - - /** - * Returns a mock object for the specified trait with all abstract methods - * of the trait mocked. Concrete methods to mock can be specified with the - * `$mockedMethods` parameter. - * - * @param string $traitName - * @param string $mockClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param array $mockedMethods - * @param bool $cloneArguments - * - * @throws \ReflectionException - * @throws RuntimeException - * @throws Exception - * - * @return MockObject - */ - public function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true) - { - if (!\is_string($traitName)) { - throw InvalidArgumentHelper::factory(1, 'string'); - } - - if (!\is_string($mockClassName)) { - throw InvalidArgumentHelper::factory(3, 'string'); - } - - if (!\trait_exists($traitName, $callAutoload)) { - throw new RuntimeException( - \sprintf( - 'Trait "%s" does not exist.', - $traitName - ) - ); - } - - $className = $this->generateClassName( - $traitName, - '', - 'Trait_' - ); - - $classTemplate = $this->getTemplate('trait_class.tpl'); - - $classTemplate->setVar( - [ - 'prologue' => 'abstract ', - 'class_name' => $className['className'], - 'trait_name' => $traitName, - ] - ); - - $this->evalClass( - $classTemplate->render(), - $className['className'] - ); - - return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments); - } - - /** - * Returns an object for the specified trait. - * - * @param string $traitName - * @param string $traitClassName - * @param bool $callOriginalConstructor - * @param bool $callOriginalClone - * @param bool $callAutoload - * - * @throws \ReflectionException - * @throws RuntimeException - * @throws Exception - * - * @return object - */ - public function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true) - { - if (!\is_string($traitName)) { - throw InvalidArgumentHelper::factory(1, 'string'); - } - - if (!\is_string($traitClassName)) { - throw InvalidArgumentHelper::factory(3, 'string'); - } - - if (!\trait_exists($traitName, $callAutoload)) { - throw new RuntimeException( - \sprintf( - 'Trait "%s" does not exist.', - $traitName - ) - ); - } - - $className = $this->generateClassName( - $traitName, - $traitClassName, - 'Trait_' - ); - - $classTemplate = $this->getTemplate('trait_class.tpl'); - - $classTemplate->setVar( - [ - 'prologue' => '', - 'class_name' => $className['className'], - 'trait_name' => $traitName, - ] - ); - - return $this->getObject($classTemplate->render(), $className['className']); - } - - /** - * @param array|string $type - * @param array $methods - * @param string $mockClassName - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param bool $cloneArguments - * @param bool $callOriginalMethods - * - * @throws \ReflectionException - * @throws \PHPUnit\Framework\MockObject\RuntimeException - * - * @return array - */ - public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false) - { - if (\is_array($type)) { - \sort($type); - } - - if ($mockClassName !== '') { - return $this->generateMock( - $type, - $methods, - $mockClassName, - $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods - ); - } - $key = \md5( - \is_array($type) ? \implode('_', $type) : $type . - \serialize($methods) . - \serialize($callOriginalClone) . - \serialize($cloneArguments) . - \serialize($callOriginalMethods) - ); - - if (!isset(self::$cache[$key])) { - self::$cache[$key] = $this->generateMock( - $type, - $methods, - $mockClassName, - $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods - ); - } - - return self::$cache[$key]; - } - - /** - * @param string $wsdlFile - * @param string $className - * - * @throws RuntimeException - * - * @return string - */ - public function generateClassFromWsdl($wsdlFile, $className, array $methods = [], array $options = []) - { - if (!\extension_loaded('soap')) { - throw new RuntimeException( - 'The SOAP extension is required to generate a mock object from WSDL.' - ); - } - - $options = \array_merge($options, ['cache_wsdl' => \WSDL_CACHE_NONE]); - $client = new SoapClient($wsdlFile, $options); - $_methods = \array_unique($client->__getFunctions()); - unset($client); - - \sort($_methods); - - $methodTemplate = $this->getTemplate('wsdl_method.tpl'); - $methodsBuffer = ''; - - foreach ($_methods as $method) { - \preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\(/', $method, $matches, \PREG_OFFSET_CAPTURE); - $lastFunction = \array_pop($matches[0]); - $nameStart = $lastFunction[1]; - $nameEnd = $nameStart + \strlen($lastFunction[0]) - 1; - $name = \str_replace('(', '', $lastFunction[0]); - - if (empty($methods) || \in_array($name, $methods, true)) { - $args = \explode( - ',', - \str_replace(')', '', \substr($method, $nameEnd + 1)) - ); - - foreach (\range(0, \count($args) - 1) as $i) { - $args[$i] = \substr($args[$i], \strpos($args[$i], '$')); - } - - $methodTemplate->setVar( - [ - 'method_name' => $name, - 'arguments' => \implode(', ', $args), - ] - ); - - $methodsBuffer .= $methodTemplate->render(); - } - } - - $optionsBuffer = '['; - - foreach ($options as $key => $value) { - $optionsBuffer .= $key . ' => ' . $value; - } - - $optionsBuffer .= ']'; - - $classTemplate = $this->getTemplate('wsdl_class.tpl'); - $namespace = ''; - - if (\strpos($className, '\\') !== false) { - $parts = \explode('\\', $className); - $className = \array_pop($parts); - $namespace = 'namespace ' . \implode('\\', $parts) . ';' . "\n\n"; - } - - $classTemplate->setVar( - [ - 'namespace' => $namespace, - 'class_name' => $className, - 'wsdl' => $wsdlFile, - 'options' => $optionsBuffer, - 'methods' => $methodsBuffer, - ] - ); - - return $classTemplate->render(); - } - - /** - * @param string $className - * - * @throws \ReflectionException - * - * @return string[] - */ - public function getClassMethods($className): array - { - $class = new ReflectionClass($className); - $methods = []; - - foreach ($class->getMethods() as $method) { - if ($method->isPublic() || $method->isAbstract()) { - $methods[] = $method->getName(); - } - } - - return $methods; - } - - /** - * @throws \ReflectionException - * - * @return MockMethod[] - */ - public function mockClassMethods(string $className, bool $callOriginalMethods, bool $cloneArguments): array - { - $class = new ReflectionClass($className); - $methods = []; - - foreach ($class->getMethods() as $method) { - if (($method->isPublic() || $method->isAbstract()) && $this->canMockMethod($method)) { - $methods[] = MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments); - } - } - - return $methods; - } - - /** - * @param string $code - * @param string $className - * @param array|string $type - * @param bool $callOriginalConstructor - * @param bool $callAutoload - * @param bool $callOriginalMethods - * @param object $proxyTarget - * @param bool $returnValueGeneration - * - * @throws \ReflectionException - * @throws RuntimeException - * - * @return MockObject - */ - private function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = [], $callOriginalMethods = false, $proxyTarget = null, $returnValueGeneration = true) - { - $this->evalClass($code, $className); - - if ($callOriginalConstructor && - \is_string($type) && - !\interface_exists($type, $callAutoload)) { - if (\count($arguments) === 0) { - $object = new $className; - } else { - $class = new ReflectionClass($className); - $object = $class->newInstanceArgs($arguments); - } - } else { - try { - $instantiator = new Instantiator; - $object = $instantiator->instantiate($className); - } catch (InstantiatorException $exception) { - throw new RuntimeException($exception->getMessage()); - } - } - - if ($callOriginalMethods) { - if (!\is_object($proxyTarget)) { - if (\count($arguments) === 0) { - $proxyTarget = new $type; - } else { - $class = new ReflectionClass($type); - $proxyTarget = $class->newInstanceArgs($arguments); - } - } - - $object->__phpunit_setOriginalObject($proxyTarget); - } - - if ($object instanceof MockObject) { - $object->__phpunit_setReturnValueGeneration($returnValueGeneration); - } - - return $object; - } - - /** - * @param string $code - * @param string $className - */ - private function evalClass($code, $className): void - { - if (!\class_exists($className, false)) { - eval($code); - } - } - - /** - * @param array|string $type - * @param null|array $explicitMethods - * @param string $mockClassName - * @param bool $callOriginalClone - * @param bool $callAutoload - * @param bool $cloneArguments - * @param bool $callOriginalMethods - * - * @throws \InvalidArgumentException - * @throws \ReflectionException - * @throws RuntimeException - * - * @return array - */ - private function generateMock($type, $explicitMethods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods) - { - $classTemplate = $this->getTemplate('mocked_class.tpl'); - - $additionalInterfaces = []; - $cloneTemplate = ''; - $isClass = false; - $isInterface = false; - $class = null; - $mockMethods = new MockMethodSet; - - if (\is_array($type)) { - $interfaceMethods = []; - - foreach ($type as $_type) { - if (!\interface_exists($_type, $callAutoload)) { - throw new RuntimeException( - \sprintf( - 'Interface "%s" does not exist.', - $_type - ) - ); - } - - $additionalInterfaces[] = $_type; - $typeClass = new ReflectionClass($_type); - - foreach ($this->getClassMethods($_type) as $method) { - if (\in_array($method, $interfaceMethods, true)) { - throw new RuntimeException( - \sprintf( - 'Duplicate method "%s" not allowed.', - $method - ) - ); - } - - $methodReflection = $typeClass->getMethod($method); - - if ($this->canMockMethod($methodReflection)) { - $mockMethods->addMethods( - MockMethod::fromReflection($methodReflection, $callOriginalMethods, $cloneArguments) - ); - - $interfaceMethods[] = $method; - } - } - } - - unset($interfaceMethods); - } - - $mockClassName = $this->generateClassName( - $type, - $mockClassName, - 'Mock_' - ); - - if (\class_exists($mockClassName['fullClassName'], $callAutoload)) { - $isClass = true; - } elseif (\interface_exists($mockClassName['fullClassName'], $callAutoload)) { - $isInterface = true; - } - - if (!$isClass && !$isInterface) { - $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n"; - - if (!empty($mockClassName['namespaceName'])) { - $prologue = 'namespace ' . $mockClassName['namespaceName'] . - " {\n\n" . $prologue . "}\n\n" . - "namespace {\n\n"; - - $epilogue = "\n\n}"; - } - - $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); - } else { - $class = new ReflectionClass($mockClassName['fullClassName']); - - if ($class->isFinal()) { - throw new RuntimeException( - \sprintf( - 'Class "%s" is declared "final" and cannot be mocked.', - $mockClassName['fullClassName'] - ) - ); - } - - // @see https://github.com/sebastianbergmann/phpunit/issues/2995 - if ($isInterface && $class->implementsInterface(\Throwable::class)) { - $additionalInterfaces[] = $class->getName(); - $isInterface = false; - - $mockClassName = $this->generateClassName( - \Exception::class, - '', - 'Mock_' - ); - - $class = new ReflectionClass($mockClassName['fullClassName']); - } - - // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103 - if ($isInterface && $class->implementsInterface(Traversable::class) && - !$class->implementsInterface(Iterator::class) && - !$class->implementsInterface(IteratorAggregate::class)) { - $additionalInterfaces[] = Iterator::class; - - $mockMethods->addMethods( - ...$this->mockClassMethods(Iterator::class, $callOriginalMethods, $cloneArguments) - ); - } - - if ($class->hasMethod('__clone')) { - $cloneMethod = $class->getMethod('__clone'); - - if (!$cloneMethod->isFinal()) { - if ($callOriginalClone && !$isInterface) { - $cloneTemplate = $this->getTemplate('unmocked_clone.tpl'); - } else { - $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); - } - } - } else { - $cloneTemplate = $this->getTemplate('mocked_clone.tpl'); - } - } - - if (\is_object($cloneTemplate)) { - $cloneTemplate = $cloneTemplate->render(); - } - - if ($explicitMethods === [] && - ($isClass || $isInterface)) { - $mockMethods->addMethods( - ...$this->mockClassMethods($mockClassName['fullClassName'], $callOriginalMethods, $cloneArguments) - ); - } - - if (\is_array($explicitMethods)) { - foreach ($explicitMethods as $methodName) { - if ($class !== null && $class->hasMethod($methodName)) { - $method = $class->getMethod($methodName); - - if ($this->canMockMethod($method)) { - $mockMethods->addMethods( - MockMethod::fromReflection($method, $callOriginalMethods, $cloneArguments) - ); - } - } else { - $mockMethods->addMethods( - MockMethod::fromName( - $mockClassName['fullClassName'], - $methodName, - $cloneArguments - ) - ); - } - } - } - - $mockedMethods = ''; - $configurable = []; - - /** @var MockMethod $mockMethod */ - foreach ($mockMethods->asArray() as $mockMethod) { - $mockedMethods .= $mockMethod->generateCode(); - $configurable[] = \strtolower($mockMethod->getName()); - } - - $method = ''; - - if (!$mockMethods->hasMethod('method') && (!isset($class) || !$class->hasMethod('method'))) { - $methodTemplate = $this->getTemplate('mocked_class_method.tpl'); - - $method = $methodTemplate->render(); - } - - $classTemplate->setVar( - [ - 'prologue' => $prologue ?? '', - 'epilogue' => $epilogue ?? '', - 'class_declaration' => $this->generateMockClassDeclaration( - $mockClassName, - $isInterface, - $additionalInterfaces - ), - 'clone' => $cloneTemplate, - 'mock_class_name' => $mockClassName['className'], - 'mocked_methods' => $mockedMethods, - 'method' => $method, - 'configurable' => '[' . \implode( - ', ', - \array_map( - function ($m) { - return '\'' . $m . '\''; - }, - $configurable - ) - ) . ']', - ] - ); - - return [ - 'code' => $classTemplate->render(), - 'mockClassName' => $mockClassName['className'], - ]; - } - - /** - * @param array|string $type - * @param string $className - * @param string $prefix - * - * @return array - */ - private function generateClassName($type, $className, $prefix) - { - if (\is_array($type)) { - $type = \implode('_', $type); - } - - if ($type[0] === '\\') { - $type = \substr($type, 1); - } - - $classNameParts = \explode('\\', $type); - - if (\count($classNameParts) > 1) { - $type = \array_pop($classNameParts); - $namespaceName = \implode('\\', $classNameParts); - $fullClassName = $namespaceName . '\\' . $type; - } else { - $namespaceName = ''; - $fullClassName = $type; - } - - if ($className === '') { - do { - $className = $prefix . $type . '_' . - \substr(\md5(\mt_rand()), 0, 8); - } while (\class_exists($className, false)); - } - - return [ - 'className' => $className, - 'originalClassName' => $type, - 'fullClassName' => $fullClassName, - 'namespaceName' => $namespaceName, - ]; - } - - /** - * @param bool $isInterface - * - * @return string - */ - private function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = []) - { - $buffer = 'class '; - - $additionalInterfaces[] = MockObject::class; - $interfaces = \implode(', ', $additionalInterfaces); - - if ($isInterface) { - $buffer .= \sprintf( - '%s implements %s', - $mockClassName['className'], - $interfaces - ); - - if (!\in_array($mockClassName['originalClassName'], $additionalInterfaces)) { - $buffer .= ', '; - - if (!empty($mockClassName['namespaceName'])) { - $buffer .= $mockClassName['namespaceName'] . '\\'; - } - - $buffer .= $mockClassName['originalClassName']; - } - } else { - $buffer .= \sprintf( - '%s extends %s%s implements %s', - $mockClassName['className'], - !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '', - $mockClassName['originalClassName'], - $interfaces - ); - } - - return $buffer; - } - - /** - * @return bool - */ - private function canMockMethod(ReflectionMethod $method) - { - return !($method->isConstructor() || $method->isFinal() || $method->isPrivate() || $this->isMethodNameBlacklisted($method->getName())); - } - - /** - * Returns whether a method name is blacklisted - * - * @param string $name - * - * @return bool - */ - private function isMethodNameBlacklisted($name) - { - return isset(self::BLACKLISTED_METHOD_NAMES[$name]); - } - - /** - * @param string $template - * - * @throws \InvalidArgumentException - * - * @return Text_Template - */ - private function getTemplate($template) - { - $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template; - - if (!isset(self::$templates[$filename])) { - self::$templates[$filename] = new Text_Template($filename); - } - - return self::$templates[$filename]; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; -use PHPUnit\Framework\MockObject\Matcher\Invocation; - -/** - * Interface for all mock objects which are generated by - * MockBuilder. - * - * @method InvocationMocker method($constraint) - * - * @deprecated Use PHPUnit\Framework\MockObject\MockObject instead - */ -interface PHPUnit_Framework_MockObject_MockObject /*extends Verifiable*/ -{ - /** - * @return InvocationMocker - */ - public function __phpunit_setOriginalObject($originalObject); - - /** - * @return InvocationMocker - */ - public function __phpunit_getInvocationMocker(); - - /** - * Verifies that the current expectation is valid. If everything is OK the - * code should just return, if not it must throw an exception. - * - * @throws ExpectationFailedException - */ - public function __phpunit_verify(bool $unsetInvocationMocker = true); - - /** - * @return bool - */ - public function __phpunit_hasMatchers(); - - public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration); - - /** - * Registers a new expectation in the mock object and returns the match - * object which can be infused with further details. - * - * @return InvocationMocker - */ - public function expects(Invocation $matcher); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class SkippedTestSuiteError extends AssertionFailedError implements SkippedTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -interface RiskyTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * An incomplete test case - */ -class IncompleteTestCase extends TestCase -{ - /** - * @var string - */ - protected $message = ''; - - /** - * @var bool - */ - protected $backupGlobals = false; - - /** - * @var bool - */ - protected $backupStaticAttributes = false; - - /** - * @var bool - */ - protected $runTestInSeparateProcess = false; - - /** - * @var bool - */ - protected $useErrorHandler = false; - - /** - * @var bool - */ - protected $useOutputBuffering = false; - - public function __construct(string $className, string $methodName, string $message = '') - { - parent::__construct($className . '::' . $methodName); - - $this->message = $message; - } - - public function getMessage(): string - { - return $this->message; - } - - /** - * Returns a string representation of the test case. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function toString(): string - { - return $this->getName(); - } - - /** - * @throws Exception - */ - protected function runTest(): void - { - $this->markTestIncomplete($this->message); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use RecursiveIterator; - -/** - * Iterator for test suites. - */ -final class TestSuiteIterator implements RecursiveIterator -{ - /** - * @var int - */ - private $position; - - /** - * @var Test[] - */ - private $tests; - - public function __construct(TestSuite $testSuite) - { - $this->tests = $testSuite->tests(); - } - - /** - * Rewinds the Iterator to the first element. - */ - public function rewind(): void - { - $this->position = 0; - } - - /** - * Checks if there is a current element after calls to rewind() or next(). - */ - public function valid(): bool - { - return $this->position < \count($this->tests); - } - - /** - * Returns the key of the current element. - */ - public function key(): int - { - return $this->position; - } - - /** - * Returns the current element. - */ - public function current(): Test - { - return $this->valid() ? $this->tests[$this->position] : null; - } - - /** - * Moves forward to next element. - */ - public function next(): void - { - $this->position++; - } - - /** - * Returns the sub iterator for the current element. - */ - public function getChildren(): self - { - return new self( - $this->tests[$this->position] - ); - } - - /** - * Checks whether the current element has children. - */ - public function hasChildren(): bool - { - return $this->tests[$this->position] instanceof TestSuite; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * Interface for classes that can return a description of itself. - */ -interface SelfDescribing -{ - /** - * Returns a string representation of the object. - */ - public function toString(): string; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use AssertionError; -use Countable; -use Error; -use PHPUnit\Framework\MockObject\Exception as MockObjectException; -use PHPUnit\Util\Blacklist; -use PHPUnit\Util\ErrorHandler; -use PHPUnit\Util\Printer; -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException as OriginalCoveredCodeNotExecutedException; -use SebastianBergmann\CodeCoverage\Exception as OriginalCodeCoverageException; -use SebastianBergmann\CodeCoverage\MissingCoversAnnotationException as OriginalMissingCoversAnnotationException; -use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException; -use SebastianBergmann\Invoker\Invoker; -use SebastianBergmann\Invoker\TimeoutException; -use SebastianBergmann\ResourceOperations\ResourceOperations; -use SebastianBergmann\Timer\Timer; -use Throwable; - -/** - * A TestResult collects the results of executing a test case. - */ -class TestResult implements Countable -{ - /** - * @var array - */ - protected $passed = []; - - /** - * @var TestFailure[] - */ - protected $errors = []; - - /** - * @var TestFailure[] - */ - protected $failures = []; - - /** - * @var TestFailure[] - */ - protected $warnings = []; - - /** - * @var TestFailure[] - */ - protected $notImplemented = []; - - /** - * @var TestFailure[] - */ - protected $risky = []; - - /** - * @var TestFailure[] - */ - protected $skipped = []; - - /** - * @var TestListener[] - */ - protected $listeners = []; - - /** - * @var int - */ - protected $runTests = 0; - - /** - * @var float - */ - protected $time = 0; - - /** - * @var TestSuite - */ - protected $topTestSuite; - - /** - * Code Coverage information. - * - * @var CodeCoverage - */ - protected $codeCoverage; - - /** - * @var bool - */ - protected $convertErrorsToExceptions = true; - - /** - * @var bool - */ - protected $stop = false; - - /** - * @var bool - */ - protected $stopOnError = false; - - /** - * @var bool - */ - protected $stopOnFailure = false; - - /** - * @var bool - */ - protected $stopOnWarning = false; - - /** - * @var bool - */ - protected $beStrictAboutTestsThatDoNotTestAnything = true; - - /** - * @var bool - */ - protected $beStrictAboutOutputDuringTests = false; - - /** - * @var bool - */ - protected $beStrictAboutTodoAnnotatedTests = false; - - /** - * @var bool - */ - protected $beStrictAboutResourceUsageDuringSmallTests = false; - - /** - * @var bool - */ - protected $enforceTimeLimit = false; - - /** - * @var int - */ - protected $timeoutForSmallTests = 1; - - /** - * @var int - */ - protected $timeoutForMediumTests = 10; - - /** - * @var int - */ - protected $timeoutForLargeTests = 60; - - /** - * @var bool - */ - protected $stopOnRisky = false; - - /** - * @var bool - */ - protected $stopOnIncomplete = false; - - /** - * @var bool - */ - protected $stopOnSkipped = false; - - /** - * @var bool - */ - protected $lastTestFailed = false; - - /** - * @var int - */ - private $defaultTimeLimit = 0; - - /** - * @var bool - */ - private $stopOnDefect = false; - - /** - * @var bool - */ - private $registerMockObjectsFromTestArgumentsRecursively = false; - - public static function isAnyCoverageRequired(TestCase $test) - { - $annotations = $test->getAnnotations(); - - // If any methods have covers, coverage must me generated - if (isset($annotations['method']['covers'])) { - return true; - } - - // If there are no explicit covers, and the test class is - // marked as covers nothing, all coverage can be skipped - if (isset($annotations['class']['coversNothing'])) { - return false; - } - - // Otherwise each test method can generate coverage - return true; - } - - /** - * Registers a TestListener. - */ - public function addListener(TestListener $listener): void - { - $this->listeners[] = $listener; - } - - /** - * Unregisters a TestListener. - */ - public function removeListener(TestListener $listener): void - { - foreach ($this->listeners as $key => $_listener) { - if ($listener === $_listener) { - unset($this->listeners[$key]); - } - } - } - - /** - * Flushes all flushable TestListeners. - */ - public function flushListeners(): void - { - foreach ($this->listeners as $listener) { - if ($listener instanceof Printer) { - $listener->flush(); - } - } - } - - /** - * Adds an error to the list of errors. - */ - public function addError(Test $test, Throwable $t, float $time): void - { - if ($t instanceof RiskyTest) { - $this->risky[] = new TestFailure($test, $t); - $notifyMethod = 'addRiskyTest'; - - if ($test instanceof TestCase) { - $test->markAsRisky(); - } - - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($t instanceof IncompleteTest) { - $this->notImplemented[] = new TestFailure($test, $t); - $notifyMethod = 'addIncompleteTest'; - - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($t instanceof SkippedTest) { - $this->skipped[] = new TestFailure($test, $t); - $notifyMethod = 'addSkippedTest'; - - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->errors[] = new TestFailure($test, $t); - $notifyMethod = 'addError'; - - if ($this->stopOnError || $this->stopOnFailure) { - $this->stop(); - } - } - - // @see https://github.com/sebastianbergmann/phpunit/issues/1953 - if ($t instanceof Error) { - $t = new ExceptionWrapper($t); - } - - foreach ($this->listeners as $listener) { - $listener->$notifyMethod($test, $t, $time); - } - - $this->lastTestFailed = true; - $this->time += $time; - } - - /** - * Adds a warning to the list of warnings. - * The passed in exception caused the warning. - */ - public function addWarning(Test $test, Warning $e, float $time): void - { - if ($this->stopOnWarning || $this->stopOnDefect) { - $this->stop(); - } - - $this->warnings[] = new TestFailure($test, $e); - - foreach ($this->listeners as $listener) { - $listener->addWarning($test, $e, $time); - } - - $this->time += $time; - } - - /** - * Adds a failure to the list of failures. - * The passed in exception caused the failure. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - if ($e instanceof RiskyTest || $e instanceof OutputError) { - $this->risky[] = new TestFailure($test, $e); - $notifyMethod = 'addRiskyTest'; - - if ($test instanceof TestCase) { - $test->markAsRisky(); - } - - if ($this->stopOnRisky || $this->stopOnDefect) { - $this->stop(); - } - } elseif ($e instanceof IncompleteTest) { - $this->notImplemented[] = new TestFailure($test, $e); - $notifyMethod = 'addIncompleteTest'; - - if ($this->stopOnIncomplete) { - $this->stop(); - } - } elseif ($e instanceof SkippedTest) { - $this->skipped[] = new TestFailure($test, $e); - $notifyMethod = 'addSkippedTest'; - - if ($this->stopOnSkipped) { - $this->stop(); - } - } else { - $this->failures[] = new TestFailure($test, $e); - $notifyMethod = 'addFailure'; - - if ($this->stopOnFailure || $this->stopOnDefect) { - $this->stop(); - } - } - - foreach ($this->listeners as $listener) { - $listener->$notifyMethod($test, $e, $time); - } - - $this->lastTestFailed = true; - $this->time += $time; - } - - /** - * Informs the result that a test suite will be started. - */ - public function startTestSuite(TestSuite $suite): void - { - if ($this->topTestSuite === null) { - $this->topTestSuite = $suite; - } - - foreach ($this->listeners as $listener) { - $listener->startTestSuite($suite); - } - } - - /** - * Informs the result that a test suite was completed. - */ - public function endTestSuite(TestSuite $suite): void - { - foreach ($this->listeners as $listener) { - $listener->endTestSuite($suite); - } - } - - /** - * Informs the result that a test will be started. - */ - public function startTest(Test $test): void - { - $this->lastTestFailed = false; - $this->runTests += \count($test); - - foreach ($this->listeners as $listener) { - $listener->startTest($test); - } - } - - /** - * Informs the result that a test was completed. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function endTest(Test $test, float $time): void - { - foreach ($this->listeners as $listener) { - $listener->endTest($test, $time); - } - - if (!$this->lastTestFailed && $test instanceof TestCase) { - $class = \get_class($test); - $key = $class . '::' . $test->getName(); - - $this->passed[$key] = [ - 'result' => $test->getResult(), - 'size' => \PHPUnit\Util\Test::getSize( - $class, - $test->getName(false) - ), - ]; - - $this->time += $time; - } - } - - /** - * Returns true if no risky test occurred. - */ - public function allHarmless(): bool - { - return $this->riskyCount() == 0; - } - - /** - * Gets the number of risky tests. - */ - public function riskyCount(): int - { - return \count($this->risky); - } - - /** - * Returns true if no incomplete test occurred. - */ - public function allCompletelyImplemented(): bool - { - return $this->notImplementedCount() == 0; - } - - /** - * Gets the number of incomplete tests. - */ - public function notImplementedCount(): int - { - return \count($this->notImplemented); - } - - /** - * Returns an array of TestFailure objects for the risky tests - * - * @return TestFailure[] - */ - public function risky(): array - { - return $this->risky; - } - - /** - * Returns an array of TestFailure objects for the incomplete tests - * - * @return TestFailure[] - */ - public function notImplemented(): array - { - return $this->notImplemented; - } - - /** - * Returns true if no test has been skipped. - */ - public function noneSkipped(): bool - { - return $this->skippedCount() == 0; - } - - /** - * Gets the number of skipped tests. - */ - public function skippedCount(): int - { - return \count($this->skipped); - } - - /** - * Returns an array of TestFailure objects for the skipped tests - * - * @return TestFailure[] - */ - public function skipped(): array - { - return $this->skipped; - } - - /** - * Gets the number of detected errors. - */ - public function errorCount(): int - { - return \count($this->errors); - } - - /** - * Returns an array of TestFailure objects for the errors - * - * @return TestFailure[] - */ - public function errors(): array - { - return $this->errors; - } - - /** - * Gets the number of detected failures. - */ - public function failureCount(): int - { - return \count($this->failures); - } - - /** - * Returns an array of TestFailure objects for the failures - * - * @return TestFailure[] - */ - public function failures(): array - { - return $this->failures; - } - - /** - * Gets the number of detected warnings. - */ - public function warningCount(): int - { - return \count($this->warnings); - } - - /** - * Returns an array of TestFailure objects for the warnings - * - * @return TestFailure[] - */ - public function warnings(): array - { - return $this->warnings; - } - - /** - * Returns the names of the tests that have passed. - */ - public function passed(): array - { - return $this->passed; - } - - /** - * Returns the (top) test suite. - */ - public function topTestSuite(): TestSuite - { - return $this->topTestSuite; - } - - /** - * Returns whether code coverage information should be collected. - */ - public function getCollectCodeCoverageInformation(): bool - { - return $this->codeCoverage !== null; - } - - /** - * Runs a TestCase. - * - * @throws CodeCoverageException - * @throws OriginalCoveredCodeNotExecutedException - * @throws OriginalMissingCoversAnnotationException - * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws \SebastianBergmann\CodeCoverage\RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function run(Test $test): void - { - Assert::resetCount(); - - $coversNothing = false; - - if ($test instanceof TestCase) { - $test->setRegisterMockObjectsFromTestArgumentsRecursively( - $this->registerMockObjectsFromTestArgumentsRecursively - ); - - $isAnyCoverageRequired = self::isAnyCoverageRequired($test); - } - - $error = false; - $failure = false; - $warning = false; - $incomplete = false; - $risky = false; - $skipped = false; - - $this->startTest($test); - - $errorHandlerSet = false; - - if ($this->convertErrorsToExceptions) { - $oldErrorHandler = \set_error_handler( - [ErrorHandler::class, 'handleError'], - \E_ALL | \E_STRICT - ); - - if ($oldErrorHandler === null) { - $errorHandlerSet = true; - } else { - \restore_error_handler(); - } - } - - $collectCodeCoverage = $this->codeCoverage !== null && - !$test instanceof WarningTestCase && - $isAnyCoverageRequired; - - if ($collectCodeCoverage) { - $this->codeCoverage->start($test); - } - - $monitorFunctions = $this->beStrictAboutResourceUsageDuringSmallTests && - !$test instanceof WarningTestCase && - $test->getSize() == \PHPUnit\Util\Test::SMALL && - \function_exists('xdebug_start_function_monitor'); - - if ($monitorFunctions) { - /* @noinspection ForgottenDebugOutputInspection */ - \xdebug_start_function_monitor(ResourceOperations::getFunctions()); - } - - Timer::start(); - - try { - if (!$test instanceof WarningTestCase && - $this->enforceTimeLimit && - ($this->defaultTimeLimit || $test->getSize() != \PHPUnit\Util\Test::UNKNOWN) && - \extension_loaded('pcntl') && \class_exists(Invoker::class)) { - switch ($test->getSize()) { - case \PHPUnit\Util\Test::SMALL: - $_timeout = $this->timeoutForSmallTests; - - break; - - case \PHPUnit\Util\Test::MEDIUM: - $_timeout = $this->timeoutForMediumTests; - - break; - - case \PHPUnit\Util\Test::LARGE: - $_timeout = $this->timeoutForLargeTests; - - break; - - case \PHPUnit\Util\Test::UNKNOWN: - $_timeout = $this->defaultTimeLimit; - - break; - } - - $invoker = new Invoker; - $invoker->invoke([$test, 'runBare'], [], $_timeout); - } else { - $test->runBare(); - } - } catch (TimeoutException $e) { - $this->addFailure( - $test, - new RiskyTestError( - $e->getMessage() - ), - $_timeout - ); - - $risky = true; - } catch (MockObjectException $e) { - $e = new Warning( - $e->getMessage() - ); - - $warning = true; - } catch (AssertionFailedError $e) { - $failure = true; - - if ($e instanceof RiskyTestError) { - $risky = true; - } elseif ($e instanceof IncompleteTestError) { - $incomplete = true; - } elseif ($e instanceof SkippedTestError) { - $skipped = true; - } - } catch (AssertionError $e) { - $test->addToAssertionCount(1); - - $failure = true; - $frame = $e->getTrace()[0]; - - $e = new AssertionFailedError( - \sprintf( - '%s in %s:%s', - $e->getMessage(), - $frame['file'], - $frame['line'] - ) - ); - } catch (Warning $e) { - $warning = true; - } catch (Exception $e) { - $error = true; - } catch (Throwable $e) { - $e = new ExceptionWrapper($e); - $error = true; - } - - $time = Timer::stop(); - $test->addToAssertionCount(Assert::getCount()); - - if ($monitorFunctions) { - $blacklist = new Blacklist; - - /** @noinspection ForgottenDebugOutputInspection */ - $functions = \xdebug_get_monitored_functions(); - - /* @noinspection ForgottenDebugOutputInspection */ - \xdebug_stop_function_monitor(); - - foreach ($functions as $function) { - if (!$blacklist->isBlacklisted($function['filename'])) { - $this->addFailure( - $test, - new RiskyTestError( - \sprintf( - '%s() used in %s:%s', - $function['function'], - $function['filename'], - $function['lineno'] - ) - ), - $time - ); - } - } - } - - if ($this->beStrictAboutTestsThatDoNotTestAnything && - $test->getNumAssertions() == 0) { - $risky = true; - } - - if ($collectCodeCoverage) { - $append = !$risky && !$incomplete && !$skipped; - $linesToBeCovered = []; - $linesToBeUsed = []; - - if ($append && $test instanceof TestCase) { - try { - $linesToBeCovered = \PHPUnit\Util\Test::getLinesToBeCovered( - \get_class($test), - $test->getName(false) - ); - - $linesToBeUsed = \PHPUnit\Util\Test::getLinesToBeUsed( - \get_class($test), - $test->getName(false) - ); - } catch (InvalidCoversTargetException $cce) { - $this->addWarning( - $test, - new Warning( - $cce->getMessage() - ), - $time - ); - } - } - - try { - $this->codeCoverage->stop( - $append, - $linesToBeCovered, - $linesToBeUsed - ); - } catch (UnintentionallyCoveredCodeException $cce) { - $this->addFailure( - $test, - new UnintentionallyCoveredCodeError( - 'This test executed code that is not listed as code to be covered or used:' . - \PHP_EOL . $cce->getMessage() - ), - $time - ); - } catch (OriginalCoveredCodeNotExecutedException $cce) { - $this->addFailure( - $test, - new CoveredCodeNotExecutedException( - 'This test did not execute all the code that is listed as code to be covered:' . - \PHP_EOL . $cce->getMessage() - ), - $time - ); - } catch (OriginalMissingCoversAnnotationException $cce) { - if ($linesToBeCovered !== false) { - $this->addFailure( - $test, - new MissingCoversAnnotationException( - 'This test does not have a @covers annotation but is expected to have one' - ), - $time - ); - } - } catch (OriginalCodeCoverageException $cce) { - $error = true; - - $e = $e ?? $cce; - } - } - - if ($errorHandlerSet === true) { - \restore_error_handler(); - } - - if ($error === true) { - $this->addError($test, $e, $time); - } elseif ($failure === true) { - $this->addFailure($test, $e, $time); - } elseif ($warning === true) { - $this->addWarning($test, $e, $time); - } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && - !$test->doesNotPerformAssertions() && - $test->getNumAssertions() == 0) { - $reflected = new \ReflectionClass($test); - $name = $test->getName(false); - - if ($name && $reflected->hasMethod($name)) { - $reflected = $reflected->getMethod($name); - } - $this->addFailure( - $test, - new RiskyTestError(\sprintf( - "This test did not perform any assertions\n\n%s:%d", - $reflected->getFileName(), - $reflected->getStartLine() - )), - $time - ); - } elseif ($this->beStrictAboutTestsThatDoNotTestAnything && - $test->doesNotPerformAssertions() && - $test->getNumAssertions() > 0) { - $this->addFailure( - $test, - new RiskyTestError(\sprintf( - 'This test is annotated with "@doesNotPerformAssertions" but performed %d assertions', - $test->getNumAssertions() - )), - $time - ); - } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) { - $this->addFailure( - $test, - new OutputError( - \sprintf( - 'This test printed output: %s', - $test->getActualOutput() - ) - ), - $time - ); - } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof TestCase) { - $annotations = $test->getAnnotations(); - - if (isset($annotations['method']['todo'])) { - $this->addFailure( - $test, - new RiskyTestError( - 'Test method is annotated with @todo' - ), - $time - ); - } - } - - $this->endTest($test, $time); - } - - /** - * Gets the number of run tests. - */ - public function count(): int - { - return $this->runTests; - } - - /** - * Checks whether the test run should stop. - */ - public function shouldStop(): bool - { - return $this->stop; - } - - /** - * Marks that the test run should stop. - */ - public function stop(): void - { - $this->stop = true; - } - - /** - * Returns the code coverage object. - */ - public function getCodeCoverage(): ?CodeCoverage - { - return $this->codeCoverage; - } - - /** - * Sets the code coverage object. - */ - public function setCodeCoverage(CodeCoverage $codeCoverage): void - { - $this->codeCoverage = $codeCoverage; - } - - /** - * Enables or disables the error-to-exception conversion. - */ - public function convertErrorsToExceptions(bool $flag): void - { - $this->convertErrorsToExceptions = $flag; - } - - /** - * Returns the error-to-exception conversion setting. - */ - public function getConvertErrorsToExceptions(): bool - { - return $this->convertErrorsToExceptions; - } - - /** - * Enables or disables the stopping when an error occurs. - */ - public function stopOnError(bool $flag): void - { - $this->stopOnError = $flag; - } - - /** - * Enables or disables the stopping when a failure occurs. - */ - public function stopOnFailure(bool $flag): void - { - $this->stopOnFailure = $flag; - } - - /** - * Enables or disables the stopping when a warning occurs. - */ - public function stopOnWarning(bool $flag): void - { - $this->stopOnWarning = $flag; - } - - public function beStrictAboutTestsThatDoNotTestAnything(bool $flag): void - { - $this->beStrictAboutTestsThatDoNotTestAnything = $flag; - } - - public function isStrictAboutTestsThatDoNotTestAnything(): bool - { - return $this->beStrictAboutTestsThatDoNotTestAnything; - } - - public function beStrictAboutOutputDuringTests(bool $flag): void - { - $this->beStrictAboutOutputDuringTests = $flag; - } - - public function isStrictAboutOutputDuringTests(): bool - { - return $this->beStrictAboutOutputDuringTests; - } - - public function beStrictAboutResourceUsageDuringSmallTests(bool $flag): void - { - $this->beStrictAboutResourceUsageDuringSmallTests = $flag; - } - - public function isStrictAboutResourceUsageDuringSmallTests(): bool - { - return $this->beStrictAboutResourceUsageDuringSmallTests; - } - - public function enforceTimeLimit(bool $flag): void - { - $this->enforceTimeLimit = $flag; - } - - public function enforcesTimeLimit(): bool - { - return $this->enforceTimeLimit; - } - - public function beStrictAboutTodoAnnotatedTests(bool $flag): void - { - $this->beStrictAboutTodoAnnotatedTests = $flag; - } - - public function isStrictAboutTodoAnnotatedTests(): bool - { - return $this->beStrictAboutTodoAnnotatedTests; - } - - /** - * Enables or disables the stopping for risky tests. - */ - public function stopOnRisky(bool $flag): void - { - $this->stopOnRisky = $flag; - } - - /** - * Enables or disables the stopping for incomplete tests. - */ - public function stopOnIncomplete(bool $flag): void - { - $this->stopOnIncomplete = $flag; - } - - /** - * Enables or disables the stopping for skipped tests. - */ - public function stopOnSkipped(bool $flag): void - { - $this->stopOnSkipped = $flag; - } - - /** - * Enables or disables the stopping for defects: error, failure, warning - */ - public function stopOnDefect(bool $flag): void - { - $this->stopOnDefect = $flag; - } - - /** - * Returns the time spent running the tests. - */ - public function time(): float - { - return $this->time; - } - - /** - * Returns whether the entire test was successful or not. - */ - public function wasSuccessful(): bool - { - return $this->wasSuccessfulIgnoringWarnings() && empty($this->warnings); - } - - public function wasSuccessfulIgnoringWarnings(): bool - { - return empty($this->errors) && empty($this->failures); - } - - /** - * Sets the default timeout for tests - */ - public function setDefaultTimeLimit(int $timeout): void - { - $this->defaultTimeLimit = $timeout; - } - - /** - * Sets the timeout for small tests. - */ - public function setTimeoutForSmallTests(int $timeout): void - { - $this->timeoutForSmallTests = $timeout; - } - - /** - * Sets the timeout for medium tests. - */ - public function setTimeoutForMediumTests(int $timeout): void - { - $this->timeoutForMediumTests = $timeout; - } - - /** - * Sets the timeout for large tests. - */ - public function setTimeoutForLargeTests(int $timeout): void - { - $this->timeoutForLargeTests = $timeout; - } - - /** - * Returns the set timeout for large tests. - */ - public function getTimeoutForLargeTests(): int - { - return $this->timeoutForLargeTests; - } - - public function setRegisterMockObjectsFromTestArgumentsRecursively(bool $flag): void - { - $this->registerMockObjectsFromTestArgumentsRecursively = $flag; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class CoveredCodeNotExecutedException extends RiskyTestError -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class CodeCoverageException extends Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class DataProviderTestSuite extends TestSuite -{ - /** - * @var string[] - */ - private $dependencies = []; - - /** - * @param string[] $dependencies - */ - public function setDependencies(array $dependencies): void - { - $this->dependencies = $dependencies; - - foreach ($this->tests as $test) { - $test->setDependencies($dependencies); - } - } - - public function getDependencies(): array - { - return $this->dependencies; - } - - public function hasDependencies(): bool - { - return \count($this->dependencies) > 0; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * A warning. - */ -class WarningTestCase extends TestCase -{ - /** - * @var string - */ - protected $message = ''; - - /** - * @var bool - */ - protected $backupGlobals = false; - - /** - * @var bool - */ - protected $backupStaticAttributes = false; - - /** - * @var bool - */ - protected $runTestInSeparateProcess = false; - - /** - * @var bool - */ - protected $useErrorHandler = false; - - /** - * @param string $message - */ - public function __construct($message = '') - { - $this->message = $message; - parent::__construct('Warning'); - } - - public function getMessage(): string - { - return $this->message; - } - - /** - * Returns a string representation of the test case. - */ - public function toString(): string - { - return 'Warning'; - } - - /** - * @throws Exception - */ - protected function runTest(): void - { - throw new Warning($this->message); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\Constraint\ArrayHasKey; -use PHPUnit\Framework\Constraint\Attribute; -use PHPUnit\Framework\Constraint\Callback; -use PHPUnit\Framework\Constraint\ClassHasAttribute; -use PHPUnit\Framework\Constraint\ClassHasStaticAttribute; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\Count; -use PHPUnit\Framework\Constraint\DirectoryExists; -use PHPUnit\Framework\Constraint\FileExists; -use PHPUnit\Framework\Constraint\GreaterThan; -use PHPUnit\Framework\Constraint\IsAnything; -use PHPUnit\Framework\Constraint\IsEmpty; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\Constraint\IsFalse; -use PHPUnit\Framework\Constraint\IsFinite; -use PHPUnit\Framework\Constraint\IsIdentical; -use PHPUnit\Framework\Constraint\IsInfinite; -use PHPUnit\Framework\Constraint\IsInstanceOf; -use PHPUnit\Framework\Constraint\IsJson; -use PHPUnit\Framework\Constraint\IsNan; -use PHPUnit\Framework\Constraint\IsNull; -use PHPUnit\Framework\Constraint\IsReadable; -use PHPUnit\Framework\Constraint\IsTrue; -use PHPUnit\Framework\Constraint\IsType; -use PHPUnit\Framework\Constraint\IsWritable; -use PHPUnit\Framework\Constraint\LessThan; -use PHPUnit\Framework\Constraint\LogicalAnd; -use PHPUnit\Framework\Constraint\LogicalNot; -use PHPUnit\Framework\Constraint\LogicalOr; -use PHPUnit\Framework\Constraint\LogicalXor; -use PHPUnit\Framework\Constraint\ObjectHasAttribute; -use PHPUnit\Framework\Constraint\RegularExpression; -use PHPUnit\Framework\Constraint\StringContains; -use PHPUnit\Framework\Constraint\StringEndsWith; -use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; -use PHPUnit\Framework\Constraint\StringStartsWith; -use PHPUnit\Framework\Constraint\TraversableContains; -use PHPUnit\Framework\Constraint\TraversableContainsOnly; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount as AnyInvokedCountMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtIndex as InvokedAtIndexMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastCount as InvokedAtLeastCountMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedAtMostCount as InvokedAtMostCountMatcher; -use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; -use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; - -/** - * Asserts that an array has a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertArrayHasKey($key, $array, string $message = ''): void -{ - Assert::assertArrayHasKey(...\func_get_args()); -} - -/** - * Asserts that an array has a specified subset. - * - * @param array|ArrayAccess $subset - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494 - */ -function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void -{ - Assert::assertArraySubset(...\func_get_args()); -} - -/** - * Asserts that an array does not have a specified key. - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertArrayNotHasKey($key, $array, string $message = ''): void -{ - Assert::assertArrayNotHasKey(...\func_get_args()); -} - -/** - * Asserts that a haystack contains a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void -{ - Assert::assertContains(...\func_get_args()); -} - -/** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object contains a needle. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void -{ - Assert::assertAttributeContains(...\func_get_args()); -} - -/** - * Asserts that a haystack does not contain a needle. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void -{ - Assert::assertNotContains(...\func_get_args()); -} - -/** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object does not contain a needle. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void -{ - Assert::assertAttributeNotContains(...\func_get_args()); -} - -/** - * Asserts that a haystack contains only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void -{ - Assert::assertContainsOnly(...\func_get_args()); -} - -/** - * Asserts that a haystack contains only instances of a given class name. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void -{ - Assert::assertContainsOnlyInstancesOf(...\func_get_args()); -} - -/** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object contains only values of a given type. - * - * @param object|string $haystackClassOrObject - * @param bool $isNativeType - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void -{ - Assert::assertAttributeContainsOnly(...\func_get_args()); -} - -/** - * Asserts that a haystack does not contain only values of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void -{ - Assert::assertNotContainsOnly(...\func_get_args()); -} - -/** - * Asserts that a haystack that is stored in a static attribute of a class - * or an attribute of an object does not contain only values of a given - * type. - * - * @param object|string $haystackClassOrObject - * @param bool $isNativeType - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void -{ - Assert::assertAttributeNotContainsOnly(...\func_get_args()); -} - -/** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertCount(int $expectedCount, $haystack, string $message = ''): void -{ - Assert::assertCount(...\func_get_args()); -} - -/** - * Asserts the number of elements of an array, Countable or Traversable - * that is stored in an attribute. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void -{ - Assert::assertAttributeCount(...\func_get_args()); -} - -/** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotCount(int $expectedCount, $haystack, string $message = ''): void -{ - Assert::assertNotCount(...\func_get_args()); -} - -/** - * Asserts the number of elements of an array, Countable or Traversable - * that is stored in an attribute. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void -{ - Assert::assertAttributeNotCount(...\func_get_args()); -} - -/** - * Asserts that two variables are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertEquals(...\func_get_args()); -} - -/** - * Asserts that a variable is equal to an attribute of an object. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertAttributeEquals(...\func_get_args()); -} - -/** - * Asserts that two variables are not equal. - * - * @param float $delta - * @param int $maxDepth - * @param bool $canonicalize - * @param bool $ignoreCase - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void -{ - Assert::assertNotEquals(...\func_get_args()); -} - -/** - * Asserts that a variable is not equal to an attribute of an object. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertAttributeNotEquals(...\func_get_args()); -} - -/** - * Asserts that a variable is empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertEmpty($actual, string $message = ''): void -{ - Assert::assertEmpty(...\func_get_args()); -} - -/** - * Asserts that a static attribute of a class or an attribute of an object - * is empty. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void -{ - Assert::assertAttributeEmpty(...\func_get_args()); -} - -/** - * Asserts that a variable is not empty. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotEmpty($actual, string $message = ''): void -{ - Assert::assertNotEmpty(...\func_get_args()); -} - -/** - * Asserts that a static attribute of a class or an attribute of an object - * is not empty. - * - * @param object|string $haystackClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void -{ - Assert::assertAttributeNotEmpty(...\func_get_args()); -} - -/** - * Asserts that a value is greater than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertGreaterThan($expected, $actual, string $message = ''): void -{ - Assert::assertGreaterThan(...\func_get_args()); -} - -/** - * Asserts that an attribute is greater than another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void -{ - Assert::assertAttributeGreaterThan(...\func_get_args()); -} - -/** - * Asserts that a value is greater than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void -{ - Assert::assertGreaterThanOrEqual(...\func_get_args()); -} - -/** - * Asserts that an attribute is greater than or equal to another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void -{ - Assert::assertAttributeGreaterThanOrEqual(...\func_get_args()); -} - -/** - * Asserts that a value is smaller than another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertLessThan($expected, $actual, string $message = ''): void -{ - Assert::assertLessThan(...\func_get_args()); -} - -/** - * Asserts that an attribute is smaller than another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void -{ - Assert::assertAttributeLessThan(...\func_get_args()); -} - -/** - * Asserts that a value is smaller than or equal to another value. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertLessThanOrEqual($expected, $actual, string $message = ''): void -{ - Assert::assertLessThanOrEqual(...\func_get_args()); -} - -/** - * Asserts that an attribute is smaller than or equal to another value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void -{ - Assert::assertAttributeLessThanOrEqual(...\func_get_args()); -} - -/** - * Asserts that the contents of one file is equal to the contents of another - * file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertFileEquals(...\func_get_args()); -} - -/** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertFileNotEquals(...\func_get_args()); -} - -/** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertStringEqualsFile(...\func_get_args()); -} - -/** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void -{ - Assert::assertStringNotEqualsFile(...\func_get_args()); -} - -/** - * Asserts that a file/dir is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertIsReadable(string $filename, string $message = ''): void -{ - Assert::assertIsReadable(...\func_get_args()); -} - -/** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotIsReadable(string $filename, string $message = ''): void -{ - Assert::assertNotIsReadable(...\func_get_args()); -} - -/** - * Asserts that a file/dir exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertIsWritable(string $filename, string $message = ''): void -{ - Assert::assertIsWritable(...\func_get_args()); -} - -/** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotIsWritable(string $filename, string $message = ''): void -{ - Assert::assertNotIsWritable(...\func_get_args()); -} - -/** - * Asserts that a directory exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertDirectoryExists(string $directory, string $message = ''): void -{ - Assert::assertDirectoryExists(...\func_get_args()); -} - -/** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertDirectoryNotExists(string $directory, string $message = ''): void -{ - Assert::assertDirectoryNotExists(...\func_get_args()); -} - -/** - * Asserts that a directory exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertDirectoryIsReadable(string $directory, string $message = ''): void -{ - Assert::assertDirectoryIsReadable(...\func_get_args()); -} - -/** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertDirectoryNotIsReadable(string $directory, string $message = ''): void -{ - Assert::assertDirectoryNotIsReadable(...\func_get_args()); -} - -/** - * Asserts that a directory exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertDirectoryIsWritable(string $directory, string $message = ''): void -{ - Assert::assertDirectoryIsWritable(...\func_get_args()); -} - -/** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertDirectoryNotIsWritable(string $directory, string $message = ''): void -{ - Assert::assertDirectoryNotIsWritable(...\func_get_args()); -} - -/** - * Asserts that a file exists. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileExists(string $filename, string $message = ''): void -{ - Assert::assertFileExists(...\func_get_args()); -} - -/** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileNotExists(string $filename, string $message = ''): void -{ - Assert::assertFileNotExists(...\func_get_args()); -} - -/** - * Asserts that a file exists and is readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileIsReadable(string $file, string $message = ''): void -{ - Assert::assertFileIsReadable(...\func_get_args()); -} - -/** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileNotIsReadable(string $file, string $message = ''): void -{ - Assert::assertFileNotIsReadable(...\func_get_args()); -} - -/** - * Asserts that a file exists and is writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileIsWritable(string $file, string $message = ''): void -{ - Assert::assertFileIsWritable(...\func_get_args()); -} - -/** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFileNotIsWritable(string $file, string $message = ''): void -{ - Assert::assertFileNotIsWritable(...\func_get_args()); -} - -/** - * Asserts that a condition is true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertTrue($condition, string $message = ''): void -{ - Assert::assertTrue(...\func_get_args()); -} - -/** - * Asserts that a condition is not true. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotTrue($condition, string $message = ''): void -{ - Assert::assertNotTrue(...\func_get_args()); -} - -/** - * Asserts that a condition is false. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFalse($condition, string $message = ''): void -{ - Assert::assertFalse(...\func_get_args()); -} - -/** - * Asserts that a condition is not false. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotFalse($condition, string $message = ''): void -{ - Assert::assertNotFalse(...\func_get_args()); -} - -/** - * Asserts that a variable is null. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNull($actual, string $message = ''): void -{ - Assert::assertNull(...\func_get_args()); -} - -/** - * Asserts that a variable is not null. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotNull($actual, string $message = ''): void -{ - Assert::assertNotNull(...\func_get_args()); -} - -/** - * Asserts that a variable is finite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertFinite($actual, string $message = ''): void -{ - Assert::assertFinite(...\func_get_args()); -} - -/** - * Asserts that a variable is infinite. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertInfinite($actual, string $message = ''): void -{ - Assert::assertInfinite(...\func_get_args()); -} - -/** - * Asserts that a variable is nan. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNan($actual, string $message = ''): void -{ - Assert::assertNan(...\func_get_args()); -} - -/** - * Asserts that a class has a specified attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void -{ - Assert::assertClassHasAttribute(...\func_get_args()); -} - -/** - * Asserts that a class does not have a specified attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void -{ - Assert::assertClassNotHasAttribute(...\func_get_args()); -} - -/** - * Asserts that a class has a specified static attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void -{ - Assert::assertClassHasStaticAttribute(...\func_get_args()); -} - -/** - * Asserts that a class does not have a specified static attribute. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void -{ - Assert::assertClassNotHasStaticAttribute(...\func_get_args()); -} - -/** - * Asserts that an object has a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void -{ - Assert::assertObjectHasAttribute(...\func_get_args()); -} - -/** - * Asserts that an object does not have a specified attribute. - * - * @param object $object - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void -{ - Assert::assertObjectNotHasAttribute(...\func_get_args()); -} - -/** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertSame($expected, $actual, string $message = ''): void -{ - Assert::assertSame(...\func_get_args()); -} - -/** - * Asserts that a variable and an attribute of an object have the same type - * and value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void -{ - Assert::assertAttributeSame(...\func_get_args()); -} - -/** - * Asserts that two variables do not have the same type and value. - * Used on objects, it asserts that two variables do not reference - * the same object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotSame($expected, $actual, string $message = ''): void -{ - Assert::assertNotSame(...\func_get_args()); -} - -/** - * Asserts that a variable and an attribute of an object do not have the - * same type and value. - * - * @param object|string $actualClassOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void -{ - Assert::assertAttributeNotSame(...\func_get_args()); -} - -/** - * Asserts that a variable is of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertInstanceOf(string $expected, $actual, string $message = ''): void -{ - Assert::assertInstanceOf(...\func_get_args()); -} - -/** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void -{ - Assert::assertAttributeInstanceOf(...\func_get_args()); -} - -/** - * Asserts that a variable is not of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotInstanceOf(string $expected, $actual, string $message = ''): void -{ - Assert::assertNotInstanceOf(...\func_get_args()); -} - -/** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void -{ - Assert::assertAttributeNotInstanceOf(...\func_get_args()); -} - -/** - * Asserts that a variable is of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertInternalType(string $expected, $actual, string $message = ''): void -{ - Assert::assertInternalType(...\func_get_args()); -} - -/** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void -{ - Assert::assertAttributeInternalType(...\func_get_args()); -} - -/** - * Asserts that a variable is not of a given type. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotInternalType(string $expected, $actual, string $message = ''): void -{ - Assert::assertNotInternalType(...\func_get_args()); -} - -/** - * Asserts that an attribute is of a given type. - * - * @param object|string $classOrObject - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void -{ - Assert::assertAttributeNotInternalType(...\func_get_args()); -} - -/** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertRegExp(string $pattern, string $string, string $message = ''): void -{ - Assert::assertRegExp(...\func_get_args()); -} - -/** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotRegExp(string $pattern, string $string, string $message = ''): void -{ - Assert::assertNotRegExp(...\func_get_args()); -} - -/** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertSameSize($expected, $actual, string $message = ''): void -{ - Assert::assertSameSize(...\func_get_args()); -} - -/** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is not the same. - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertNotSameSize($expected, $actual, string $message = ''): void -{ - Assert::assertNotSameSize(...\func_get_args()); -} - -/** - * Asserts that a string matches a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringMatchesFormat(string $format, string $string, string $message = ''): void -{ - Assert::assertStringMatchesFormat(...\func_get_args()); -} - -/** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void -{ - Assert::assertStringNotMatchesFormat(...\func_get_args()); -} - -/** - * Asserts that a string matches a given format file. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void -{ - Assert::assertStringMatchesFormatFile(...\func_get_args()); -} - -/** - * Asserts that a string does not match a given format string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void -{ - Assert::assertStringNotMatchesFormatFile(...\func_get_args()); -} - -/** - * Asserts that a string starts with a given prefix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringStartsWith(string $prefix, string $string, string $message = ''): void -{ - Assert::assertStringStartsWith(...\func_get_args()); -} - -/** - * Asserts that a string starts not with a given prefix. - * - * @param string $prefix - * @param string $string - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringStartsNotWith($prefix, $string, string $message = ''): void -{ - Assert::assertStringStartsNotWith(...\func_get_args()); -} - -/** - * Asserts that a string ends with a given suffix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringEndsWith(string $suffix, string $string, string $message = ''): void -{ - Assert::assertStringEndsWith(...\func_get_args()); -} - -/** - * Asserts that a string ends not with a given suffix. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void -{ - Assert::assertStringEndsNotWith(...\func_get_args()); -} - -/** - * Asserts that two XML files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void -{ - Assert::assertXmlFileEqualsXmlFile(...\func_get_args()); -} - -/** - * Asserts that two XML files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void -{ - Assert::assertXmlFileNotEqualsXmlFile(...\func_get_args()); -} - -/** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void -{ - Assert::assertXmlStringEqualsXmlFile(...\func_get_args()); -} - -/** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void -{ - Assert::assertXmlStringNotEqualsXmlFile(...\func_get_args()); -} - -/** - * Asserts that two XML documents are equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void -{ - Assert::assertXmlStringEqualsXmlString(...\func_get_args()); -} - -/** - * Asserts that two XML documents are not equal. - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void -{ - Assert::assertXmlStringNotEqualsXmlString(...\func_get_args()); -} - -/** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws AssertionFailedError - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void -{ - Assert::assertEqualXMLStructure(...\func_get_args()); -} - -/** - * Evaluates a PHPUnit\Framework\Constraint matcher object. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertThat($value, Constraint $constraint, string $message = ''): void -{ - Assert::assertThat(...\func_get_args()); -} - -/** - * Asserts that a string is a valid JSON string. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJson(string $actualJson, string $message = ''): void -{ - Assert::assertJson(...\func_get_args()); -} - -/** - * Asserts that two given JSON encoded objects or arrays are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void -{ - Assert::assertJsonStringEqualsJsonString(...\func_get_args()); -} - -/** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @param string $expectedJson - * @param string $actualJson - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void -{ - Assert::assertJsonStringNotEqualsJsonString(...\func_get_args()); -} - -/** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void -{ - Assert::assertJsonStringEqualsJsonFile(...\func_get_args()); -} - -/** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void -{ - Assert::assertJsonStringNotEqualsJsonFile(...\func_get_args()); -} - -/** - * Asserts that two JSON files are equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void -{ - Assert::assertJsonFileEqualsJsonFile(...\func_get_args()); -} - -/** - * Asserts that two JSON files are not equal. - * - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ -function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void -{ - Assert::assertJsonFileNotEqualsJsonFile(...\func_get_args()); -} - -function logicalAnd(): LogicalAnd -{ - return Assert::logicalAnd(...\func_get_args()); -} - -function logicalOr(): LogicalOr -{ - return Assert::logicalOr(...\func_get_args()); -} - -function logicalNot(Constraint $constraint): LogicalNot -{ - return Assert::logicalNot(...\func_get_args()); -} - -function logicalXor(): LogicalXor -{ - return Assert::logicalXor(...\func_get_args()); -} - -function anything(): IsAnything -{ - return Assert::anything(); -} - -function isTrue(): IsTrue -{ - return Assert::isTrue(); -} - -function callback(callable $callback): Callback -{ - return Assert::callback(...\func_get_args()); -} - -function isFalse(): IsFalse -{ - return Assert::isFalse(); -} - -function isJson(): IsJson -{ - return Assert::isJson(); -} - -function isNull(): IsNull -{ - return Assert::isNull(); -} - -function isFinite(): IsFinite -{ - return Assert::isFinite(); -} - -function isInfinite(): IsInfinite -{ - return Assert::isInfinite(); -} - -function isNan(): IsNan -{ - return Assert::isNan(); -} - -function attribute(Constraint $constraint, string $attributeName): Attribute -{ - return Assert::attribute(...\func_get_args()); -} - -function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains -{ - return Assert::contains(...\func_get_args()); -} - -function containsOnly(string $type): TraversableContainsOnly -{ - return Assert::containsOnly(...\func_get_args()); -} - -function containsOnlyInstancesOf(string $className): TraversableContainsOnly -{ - return Assert::containsOnlyInstancesOf(...\func_get_args()); -} - -function arrayHasKey($key): ArrayHasKey -{ - return Assert::arrayHasKey(...\func_get_args()); -} - -function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual -{ - return Assert::equalTo(...\func_get_args()); -} - -function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute -{ - return Assert::attributeEqualTo(...\func_get_args()); -} - -function isEmpty(): IsEmpty -{ - return Assert::isEmpty(); -} - -function isWritable(): IsWritable -{ - return Assert::isWritable(); -} - -function isReadable(): IsReadable -{ - return Assert::isReadable(); -} - -function directoryExists(): DirectoryExists -{ - return Assert::directoryExists(); -} - -function fileExists(): FileExists -{ - return Assert::fileExists(); -} - -function greaterThan($value): GreaterThan -{ - return Assert::greaterThan(...\func_get_args()); -} - -function greaterThanOrEqual($value): LogicalOr -{ - return Assert::greaterThanOrEqual(...\func_get_args()); -} - -function classHasAttribute(string $attributeName): ClassHasAttribute -{ - return Assert::classHasAttribute(...\func_get_args()); -} - -function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute -{ - return Assert::classHasStaticAttribute(...\func_get_args()); -} - -function objectHasAttribute($attributeName): ObjectHasAttribute -{ - return Assert::objectHasAttribute(...\func_get_args()); -} - -function identicalTo($value): IsIdentical -{ - return Assert::identicalTo(...\func_get_args()); -} - -function isInstanceOf(string $className): IsInstanceOf -{ - return Assert::isInstanceOf(...\func_get_args()); -} - -function isType(string $type): IsType -{ - return Assert::isType(...\func_get_args()); -} - -function lessThan($value): LessThan -{ - return Assert::lessThan(...\func_get_args()); -} - -function lessThanOrEqual($value): LogicalOr -{ - return Assert::lessThanOrEqual(...\func_get_args()); -} - -function matchesRegularExpression(string $pattern): RegularExpression -{ - return Assert::matchesRegularExpression(...\func_get_args()); -} - -function matches(string $string): StringMatchesFormatDescription -{ - return Assert::matches(...\func_get_args()); -} - -function stringStartsWith($prefix): StringStartsWith -{ - return Assert::stringStartsWith(...\func_get_args()); -} - -function stringContains(string $string, bool $case = true): StringContains -{ - return Assert::stringContains(...\func_get_args()); -} - -function stringEndsWith(string $suffix): StringEndsWith -{ - return Assert::stringEndsWith(...\func_get_args()); -} - -function countOf(int $count): Count -{ - return Assert::countOf(...\func_get_args()); -} - -/** - * Returns a matcher that matches when the method is executed - * zero or more times. - */ -function any(): AnyInvokedCountMatcher -{ - return new AnyInvokedCountMatcher; -} - -/** - * Returns a matcher that matches when the method is never executed. - */ -function never(): InvokedCountMatcher -{ - return new InvokedCountMatcher(0); -} - -/** - * Returns a matcher that matches when the method is executed - * at least N times. - * - * @param int $requiredInvocations - */ -function atLeast($requiredInvocations): InvokedAtLeastCountMatcher -{ - return new InvokedAtLeastCountMatcher( - $requiredInvocations - ); -} - -/** - * Returns a matcher that matches when the method is executed at least once. - */ -function atLeastOnce(): InvokedAtLeastOnceMatcher -{ - return new InvokedAtLeastOnceMatcher; -} - -/** - * Returns a matcher that matches when the method is executed exactly once. - */ -function once(): InvokedCountMatcher -{ - return new InvokedCountMatcher(1); -} - -/** - * Returns a matcher that matches when the method is executed - * exactly $count times. - * - * @param int $count - */ -function exactly($count): InvokedCountMatcher -{ - return new InvokedCountMatcher($count); -} - -/** - * Returns a matcher that matches when the method is executed - * at most N times. - * - * @param int $allowedInvocations - */ -function atMost($allowedInvocations): InvokedAtMostCountMatcher -{ - return new InvokedAtMostCountMatcher($allowedInvocations); -} - -/** - * Returns a matcher that matches when the method is executed - * at the given index. - * - * @param int $index - */ -function at($index): InvokedAtIndexMatcher -{ - return new InvokedAtIndexMatcher($index); -} - -function returnValue($value): ReturnStub -{ - return new ReturnStub($value); -} - -function returnValueMap(array $valueMap): ReturnValueMapStub -{ - return new ReturnValueMapStub($valueMap); -} - -/** - * @param int $argumentIndex - */ -function returnArgument($argumentIndex): ReturnArgumentStub -{ - return new ReturnArgumentStub($argumentIndex); -} - -function returnCallback($callback): ReturnCallbackStub -{ - return new ReturnCallbackStub($callback); -} - -/** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ -function returnSelf(): ReturnSelfStub -{ - return new ReturnSelfStub; -} - -function throwException(Throwable $exception): ExceptionStub -{ - return new ExceptionStub($exception); -} - -function onConsecutiveCalls(): ConsecutiveCallsStub -{ - $args = \func_get_args(); - - return new ConsecutiveCallsStub($args); -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use Iterator; -use IteratorAggregate; -use PHPUnit\Runner\BaseTestRunner; -use PHPUnit\Runner\Filter\Factory; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Util\FileLoader; -use PHPUnit\Util\InvalidArgumentHelper; -use ReflectionClass; -use ReflectionMethod; -use Throwable; - -/** - * A TestSuite is a composite of Tests. It runs a collection of test cases. - */ -class TestSuite implements Test, SelfDescribing, IteratorAggregate -{ - /** - * Enable or disable the backup and restoration of the $GLOBALS array. - * - * @var bool - */ - protected $backupGlobals; - - /** - * Enable or disable the backup and restoration of static attributes. - * - * @var bool - */ - protected $backupStaticAttributes; - - /** - * @var bool - */ - protected $runTestInSeparateProcess = false; - - /** - * The name of the test suite. - * - * @var string - */ - protected $name = ''; - - /** - * The test groups of the test suite. - * - * @var array - */ - protected $groups = []; - - /** - * The tests in the test suite. - * - * @var TestCase[] - */ - protected $tests = []; - - /** - * The number of tests in the test suite. - * - * @var int - */ - protected $numTests = -1; - - /** - * @var bool - */ - protected $testCase = false; - - /** - * @var array - */ - protected $foundClasses = []; - - /** - * Last count of tests in this suite. - * - * @var null|int - */ - private $cachedNumTests; - - /** - * @var bool - */ - private $beStrictAboutChangesToGlobalState; - - /** - * @var Factory - */ - private $iteratorFilter; - - /** - * @var string[] - */ - private $declaredClasses; - - /** - * @param string $name - * - * @throws Exception - */ - public static function createTest(ReflectionClass $theClass, $name): Test - { - $className = $theClass->getName(); - - if (!$theClass->isInstantiable()) { - return self::warning( - \sprintf('Cannot instantiate class "%s".', $className) - ); - } - - $backupSettings = \PHPUnit\Util\Test::getBackupSettings( - $className, - $name - ); - - $preserveGlobalState = \PHPUnit\Util\Test::getPreserveGlobalStateSettings( - $className, - $name - ); - - $runTestInSeparateProcess = \PHPUnit\Util\Test::getProcessIsolationSettings( - $className, - $name - ); - - $runClassInSeparateProcess = \PHPUnit\Util\Test::getClassProcessIsolationSettings( - $className, - $name - ); - - $constructor = $theClass->getConstructor(); - - if ($constructor === null) { - throw new Exception('No valid test provided.'); - } - $parameters = $constructor->getParameters(); - - // TestCase() or TestCase($name) - if (\count($parameters) < 2) { - $test = new $className; - } // TestCase($name, $data) - else { - try { - $data = \PHPUnit\Util\Test::getProvidedData( - $className, - $name - ); - } catch (IncompleteTestError $e) { - $message = \sprintf( - 'Test for %s::%s marked incomplete by data provider', - $className, - $name - ); - - $_message = $e->getMessage(); - - if (!empty($_message)) { - $message .= "\n" . $_message; - } - - $data = self::incompleteTest($className, $name, $message); - } catch (SkippedTestError $e) { - $message = \sprintf( - 'Test for %s::%s skipped by data provider', - $className, - $name - ); - - $_message = $e->getMessage(); - - if (!empty($_message)) { - $message .= "\n" . $_message; - } - - $data = self::skipTest($className, $name, $message); - } catch (Throwable $t) { - $message = \sprintf( - 'The data provider specified for %s::%s is invalid.', - $className, - $name - ); - - $_message = $t->getMessage(); - - if (!empty($_message)) { - $message .= "\n" . $_message; - } - - $data = self::warning($message); - } - - // Test method with @dataProvider. - if (isset($data)) { - $test = new DataProviderTestSuite( - $className . '::' . $name - ); - - if (empty($data)) { - $data = self::warning( - \sprintf( - 'No tests found in suite "%s".', - $test->getName() - ) - ); - } - - $groups = \PHPUnit\Util\Test::getGroups($className, $name); - - if ($data instanceof WarningTestCase || - $data instanceof SkippedTestCase || - $data instanceof IncompleteTestCase) { - $test->addTest($data, $groups); - } else { - foreach ($data as $_dataName => $_data) { - $_test = new $className($name, $_data, $_dataName); - - /* @var TestCase $_test */ - - if ($runTestInSeparateProcess) { - $_test->setRunTestInSeparateProcess(true); - - if ($preserveGlobalState !== null) { - $_test->setPreserveGlobalState($preserveGlobalState); - } - } - - if ($runClassInSeparateProcess) { - $_test->setRunClassInSeparateProcess(true); - - if ($preserveGlobalState !== null) { - $_test->setPreserveGlobalState($preserveGlobalState); - } - } - - if ($backupSettings['backupGlobals'] !== null) { - $_test->setBackupGlobals( - $backupSettings['backupGlobals'] - ); - } - - if ($backupSettings['backupStaticAttributes'] !== null) { - $_test->setBackupStaticAttributes( - $backupSettings['backupStaticAttributes'] - ); - } - - $test->addTest($_test, $groups); - } - } - } else { - $test = new $className; - } - } - - if ($test instanceof TestCase) { - $test->setName($name); - - if ($runTestInSeparateProcess) { - $test->setRunTestInSeparateProcess(true); - - if ($preserveGlobalState !== null) { - $test->setPreserveGlobalState($preserveGlobalState); - } - } - - if ($runClassInSeparateProcess) { - $test->setRunClassInSeparateProcess(true); - - if ($preserveGlobalState !== null) { - $test->setPreserveGlobalState($preserveGlobalState); - } - } - - if ($backupSettings['backupGlobals'] !== null) { - $test->setBackupGlobals($backupSettings['backupGlobals']); - } - - if ($backupSettings['backupStaticAttributes'] !== null) { - $test->setBackupStaticAttributes( - $backupSettings['backupStaticAttributes'] - ); - } - } - - return $test; - } - - public static function isTestMethod(ReflectionMethod $method): bool - { - if (\strpos($method->name, 'test') === 0) { - return true; - } - - $annotations = \PHPUnit\Util\Test::parseAnnotations($method->getDocComment()); - - return isset($annotations['test']); - } - - /** - * Constructs a new TestSuite: - * - * - PHPUnit\Framework\TestSuite() constructs an empty TestSuite. - * - * - PHPUnit\Framework\TestSuite(ReflectionClass) constructs a - * TestSuite from the given class. - * - * - PHPUnit\Framework\TestSuite(ReflectionClass, String) - * constructs a TestSuite from the given class with the given - * name. - * - * - PHPUnit\Framework\TestSuite(String) either constructs a - * TestSuite from the given class (if the passed string is the - * name of an existing class) or constructs an empty TestSuite - * with the given name. - * - * @param string $name - * - * @throws Exception - */ - public function __construct($theClass = '', $name = '') - { - $this->declaredClasses = \get_declared_classes(); - - $argumentsValid = false; - - if (\is_object($theClass) && - $theClass instanceof ReflectionClass) { - $argumentsValid = true; - } elseif (\is_string($theClass) && - $theClass !== '' && - \class_exists($theClass, true)) { - $argumentsValid = true; - - if ($name == '') { - $name = $theClass; - } - - $theClass = new ReflectionClass($theClass); - } elseif (\is_string($theClass)) { - $this->setName($theClass); - - return; - } - - if (!$argumentsValid) { - throw new Exception; - } - - if (!$theClass->isSubclassOf(TestCase::class)) { - $this->setName($theClass); - - return; - } - - if ($name != '') { - $this->setName($name); - } else { - $this->setName($theClass->getName()); - } - - $constructor = $theClass->getConstructor(); - - if ($constructor !== null && - !$constructor->isPublic()) { - $this->addTest( - self::warning( - \sprintf( - 'Class "%s" has no public constructor.', - $theClass->getName() - ) - ) - ); - - return; - } - - foreach ($theClass->getMethods() as $method) { - if ($method->getDeclaringClass()->getName() === Assert::class) { - continue; - } - - if ($method->getDeclaringClass()->getName() === TestCase::class) { - continue; - } - - $this->addTestMethod($theClass, $method); - } - - if (empty($this->tests)) { - $this->addTest( - self::warning( - \sprintf( - 'No tests found in class "%s".', - $theClass->getName() - ) - ) - ); - } - - $this->testCase = true; - } - - /** - * Template Method that is called before the tests - * of this test suite are run. - */ - protected function setUp(): void - { - } - - /** - * Template Method that is called after the tests - * of this test suite have finished running. - */ - protected function tearDown(): void - { - } - - /** - * Returns a string representation of the test suite. - */ - public function toString(): string - { - return $this->getName(); - } - - /** - * Adds a test to the suite. - * - * @param array $groups - */ - public function addTest(Test $test, $groups = []): void - { - $class = new ReflectionClass($test); - - if (!$class->isAbstract()) { - $this->tests[] = $test; - $this->numTests = -1; - - if ($test instanceof self && empty($groups)) { - $groups = $test->getGroups(); - } - - if (empty($groups)) { - $groups = ['default']; - } - - foreach ($groups as $group) { - if (!isset($this->groups[$group])) { - $this->groups[$group] = [$test]; - } else { - $this->groups[$group][] = $test; - } - } - - if ($test instanceof TestCase) { - $test->setGroups($groups); - } - } - } - - /** - * Adds the tests from the given class to the suite. - * - * @throws Exception - */ - public function addTestSuite($testClass): void - { - if (\is_string($testClass) && \class_exists($testClass)) { - $testClass = new ReflectionClass($testClass); - } - - if (!\is_object($testClass)) { - throw InvalidArgumentHelper::factory( - 1, - 'class name or object' - ); - } - - if ($testClass instanceof self) { - $this->addTest($testClass); - } elseif ($testClass instanceof ReflectionClass) { - $suiteMethod = false; - - if (!$testClass->isAbstract() && $testClass->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { - $method = $testClass->getMethod( - BaseTestRunner::SUITE_METHODNAME - ); - - if ($method->isStatic()) { - $this->addTest( - $method->invoke(null, $testClass->getName()) - ); - - $suiteMethod = true; - } - } - - if (!$suiteMethod && !$testClass->isAbstract() && $testClass->isSubclassOf(TestCase::class)) { - $this->addTest(new self($testClass)); - } - } else { - throw new Exception; - } - } - - /** - * Wraps both addTest() and addTestSuite - * as well as the separate import statements for the user's convenience. - * - * If the named file cannot be read or there are no new tests that can be - * added, a PHPUnit\Framework\WarningTestCase will be created instead, - * leaving the current test run untouched. - * - * @throws Exception - */ - public function addTestFile(string $filename): void - { - if (\file_exists($filename) && \substr($filename, -5) == '.phpt') { - $this->addTest( - new PhptTestCase($filename) - ); - - return; - } - - // The given file may contain further stub classes in addition to the - // test class itself. Figure out the actual test class. - $filename = FileLoader::checkAndLoad($filename); - $newClasses = \array_diff(\get_declared_classes(), $this->declaredClasses); - - // The diff is empty in case a parent class (with test methods) is added - // AFTER a child class that inherited from it. To account for that case, - // accumulate all discovered classes, so the parent class may be found in - // a later invocation. - if (!empty($newClasses)) { - // On the assumption that test classes are defined first in files, - // process discovered classes in approximate LIFO order, so as to - // avoid unnecessary reflection. - $this->foundClasses = \array_merge($newClasses, $this->foundClasses); - $this->declaredClasses = \get_declared_classes(); - } - - // The test class's name must match the filename, either in full, or as - // a PEAR/PSR-0 prefixed short name ('NameSpace_ShortName'), or as a - // PSR-1 local short name ('NameSpace\ShortName'). The comparison must be - // anchored to prevent false-positive matches (e.g., 'OtherShortName'). - $shortName = \basename($filename, '.php'); - $shortNameRegEx = '/(?:^|_|\\\\)' . \preg_quote($shortName, '/') . '$/'; - - foreach ($this->foundClasses as $i => $className) { - if (\preg_match($shortNameRegEx, $className)) { - $class = new ReflectionClass($className); - - if ($class->getFileName() == $filename) { - $newClasses = [$className]; - unset($this->foundClasses[$i]); - - break; - } - } - } - - foreach ($newClasses as $className) { - $class = new ReflectionClass($className); - - if (\dirname($class->getFileName()) === __DIR__) { - continue; - } - - if (!$class->isAbstract()) { - if ($class->hasMethod(BaseTestRunner::SUITE_METHODNAME)) { - $method = $class->getMethod( - BaseTestRunner::SUITE_METHODNAME - ); - - if ($method->isStatic()) { - $this->addTest($method->invoke(null, $className)); - } - } elseif ($class->implementsInterface(Test::class)) { - $this->addTestSuite($class); - } - } - } - - $this->numTests = -1; - } - - /** - * Wrapper for addTestFile() that adds multiple test files. - * - * @param array|Iterator $fileNames - * - * @throws Exception - */ - public function addTestFiles($fileNames): void - { - if (!(\is_array($fileNames) || - (\is_object($fileNames) && $fileNames instanceof Iterator))) { - throw InvalidArgumentHelper::factory( - 1, - 'array or iterator' - ); - } - - foreach ($fileNames as $filename) { - $this->addTestFile((string) $filename); - } - } - - /** - * Counts the number of test cases that will be run by this test. - * - * @param bool $preferCache indicates if cache is preferred - */ - public function count($preferCache = false): int - { - if ($preferCache && $this->cachedNumTests !== null) { - return $this->cachedNumTests; - } - - $numTests = 0; - - foreach ($this as $test) { - $numTests += \count($test); - } - - $this->cachedNumTests = $numTests; - - return $numTests; - } - - /** - * Returns the name of the suite. - */ - public function getName(): string - { - return $this->name; - } - - /** - * Returns the test groups of the suite. - */ - public function getGroups(): array - { - return \array_keys($this->groups); - } - - public function getGroupDetails() - { - return $this->groups; - } - - /** - * Set tests groups of the test case - */ - public function setGroupDetails(array $groups): void - { - $this->groups = $groups; - } - - /** - * Runs the tests and collects their result in a TestResult. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function run(TestResult $result = null): TestResult - { - if ($result === null) { - $result = $this->createResult(); - } - - if (\count($this) == 0) { - return $result; - } - - $hookMethods = \PHPUnit\Util\Test::getHookMethods($this->name); - - $result->startTestSuite($this); - - try { - $this->setUp(); - - foreach ($hookMethods['beforeClass'] as $beforeClassMethod) { - if ($this->testCase === true && - \class_exists($this->name, false) && - \method_exists($this->name, $beforeClassMethod)) { - if ($missingRequirements = \PHPUnit\Util\Test::getMissingRequirements($this->name, $beforeClassMethod)) { - $this->markTestSuiteSkipped(\implode(\PHP_EOL, $missingRequirements)); - } - - \call_user_func([$this->name, $beforeClassMethod]); - } - } - } catch (SkippedTestSuiteError $error) { - foreach ($this->tests() as $test) { - $result->startTest($test); - $result->addFailure($test, $error, 0); - $result->endTest($test, 0); - } - - $this->tearDown(); - $result->endTestSuite($this); - - return $result; - } catch (Throwable $t) { - foreach ($this->tests() as $test) { - if ($result->shouldStop()) { - break; - } - - $result->startTest($test); - $result->addError($test, $t, 0); - $result->endTest($test, 0); - } - - $this->tearDown(); - $result->endTestSuite($this); - - return $result; - } - - foreach ($this as $test) { - if ($result->shouldStop()) { - break; - } - - if ($test instanceof TestCase || $test instanceof self) { - $test->setBeStrictAboutChangesToGlobalState($this->beStrictAboutChangesToGlobalState); - $test->setBackupGlobals($this->backupGlobals); - $test->setBackupStaticAttributes($this->backupStaticAttributes); - $test->setRunTestInSeparateProcess($this->runTestInSeparateProcess); - } - - $test->run($result); - } - - try { - foreach ($hookMethods['afterClass'] as $afterClassMethod) { - if ($this->testCase === true && \class_exists($this->name, false) && \method_exists( - $this->name, - $afterClassMethod - )) { - \call_user_func([$this->name, $afterClassMethod]); - } - } - } catch (Throwable $t) { - $message = "Exception in {$this->name}::$afterClassMethod" . \PHP_EOL . $t->getMessage(); - $error = new SyntheticError($message, 0, $t->getFile(), $t->getLine(), $t->getTrace()); - $test = new \Failure($afterClassMethod); - - $result->startTest($test); - $result->addFailure($test, $error, 0); - $result->endTest($test, 0); - } - - $this->tearDown(); - - $result->endTestSuite($this); - - return $result; - } - - public function setRunTestInSeparateProcess(bool $runTestInSeparateProcess): void - { - $this->runTestInSeparateProcess = $runTestInSeparateProcess; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - /** - * Returns the test at the given index. - * - * @return false|Test - */ - public function testAt(int $index) - { - if (isset($this->tests[$index])) { - return $this->tests[$index]; - } - - return false; - } - - /** - * Returns the tests as an enumeration. - */ - public function tests(): array - { - return $this->tests; - } - - /** - * Set tests of the test suite - */ - public function setTests(array $tests): void - { - $this->tests = $tests; - } - - /** - * Mark the test suite as skipped. - * - * @param string $message - * - * @throws SkippedTestSuiteError - */ - public function markTestSuiteSkipped($message = ''): void - { - throw new SkippedTestSuiteError($message); - } - - /** - * @param bool $beStrictAboutChangesToGlobalState - */ - public function setBeStrictAboutChangesToGlobalState($beStrictAboutChangesToGlobalState): void - { - if (null === $this->beStrictAboutChangesToGlobalState && \is_bool($beStrictAboutChangesToGlobalState)) { - $this->beStrictAboutChangesToGlobalState = $beStrictAboutChangesToGlobalState; - } - } - - /** - * @param bool $backupGlobals - */ - public function setBackupGlobals($backupGlobals): void - { - if (null === $this->backupGlobals && \is_bool($backupGlobals)) { - $this->backupGlobals = $backupGlobals; - } - } - - /** - * @param bool $backupStaticAttributes - */ - public function setBackupStaticAttributes($backupStaticAttributes): void - { - if (null === $this->backupStaticAttributes && \is_bool($backupStaticAttributes)) { - $this->backupStaticAttributes = $backupStaticAttributes; - } - } - - /** - * Returns an iterator for this test suite. - */ - public function getIterator(): Iterator - { - $iterator = new TestSuiteIterator($this); - - if ($this->iteratorFilter !== null) { - $iterator = $this->iteratorFilter->factory($iterator, $this); - } - - return $iterator; - } - - public function injectFilter(Factory $filter): void - { - $this->iteratorFilter = $filter; - - foreach ($this as $test) { - if ($test instanceof self) { - $test->injectFilter($filter); - } - } - } - - /** - * Creates a default TestResult object. - */ - protected function createResult(): TestResult - { - return new TestResult; - } - - /** - * @throws Exception - */ - protected function addTestMethod(ReflectionClass $class, ReflectionMethod $method): void - { - if (!$this->isTestMethod($method)) { - return; - } - - $name = $method->getName(); - - if (!$method->isPublic()) { - $this->addTest( - self::warning( - \sprintf( - 'Test method "%s" in test class "%s" is not public.', - $name, - $class->getName() - ) - ) - ); - - return; - } - - $test = self::createTest($class, $name); - - if ($test instanceof TestCase || $test instanceof DataProviderTestSuite) { - $test->setDependencies( - \PHPUnit\Util\Test::getDependencies($class->getName(), $name) - ); - } - - $this->addTest( - $test, - \PHPUnit\Util\Test::getGroups($class->getName(), $name) - ); - } - - /** - * @param string $message - */ - protected static function warning($message): WarningTestCase - { - return new WarningTestCase($message); - } - - /** - * @param string $class - * @param string $methodName - * @param string $message - */ - protected static function skipTest($class, $methodName, $message): SkippedTestCase - { - return new SkippedTestCase($class, $methodName, $message); - } - - /** - * @param string $class - * @param string $methodName - * @param string $message - */ - protected static function incompleteTest($class, $methodName, $message): IncompleteTestCase - { - return new IncompleteTestCase($class, $methodName, $message); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use PHPUnit\Util\Filter; -use Throwable; - -/** - * Wraps Exceptions thrown by code under test. - * - * Re-instantiates Exceptions thrown by user-space code to retain their original - * class names, properties, and stack traces (but without arguments). - * - * Unlike PHPUnit\Framework_\Exception, the complete stack of previous Exceptions - * is processed. - */ -class ExceptionWrapper extends Exception -{ - /** - * @var string - */ - protected $className; - - /** - * @var null|ExceptionWrapper - */ - protected $previous; - - public function __construct(Throwable $t) - { - // PDOException::getCode() is a string. - // @see https://php.net/manual/en/class.pdoexception.php#95812 - parent::__construct($t->getMessage(), (int) $t->getCode()); - $this->setOriginalException($t); - } - - /** - * @throws \InvalidArgumentException - */ - public function __toString(): string - { - $string = TestFailure::exceptionToString($this); - - if ($trace = Filter::getFilteredStacktrace($this)) { - $string .= "\n" . $trace; - } - - if ($this->previous) { - $string .= "\nCaused by\n" . $this->previous; - } - - return $string; - } - - public function getClassName(): string - { - return $this->className; - } - - public function getPreviousWrapped(): ?self - { - return $this->previous; - } - - public function setClassName(string $className): void - { - $this->className = $className; - } - - public function setOriginalException(\Throwable $t): void - { - $this->originalException($t); - - $this->className = \get_class($t); - $this->file = $t->getFile(); - $this->line = $t->getLine(); - - $this->serializableTrace = $t->getTrace(); - - foreach ($this->serializableTrace as $i => $call) { - unset($this->serializableTrace[$i]['args']); - } - - if ($t->getPrevious()) { - $this->previous = new self($t->getPrevious()); - } - } - - public function getOriginalException(): ?Throwable - { - return $this->originalException(); - } - - /** - * Method to contain static originalException to exclude it from stacktrace to prevent the stacktrace contents, - * which can be quite big, from being garbage-collected, thus blocking memory until shutdown. - * Approach works both for var_dump() and var_export() and print_r() - */ - private function originalException(Throwable $exceptionToStore = null): ?Throwable - { - static $originalExceptions; - - $instanceId = \spl_object_hash($this); - - if ($exceptionToStore) { - $originalExceptions[$instanceId] = $exceptionToStore; - } - - return $originalExceptions[$instanceId] ?? null; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * Thrown when there is a warning. - */ -class Warning extends Exception implements SelfDescribing -{ - /** - * Wrapper for getMessage() which is declared as final. - */ - public function toString(): string - { - return $this->getMessage(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -class SkippedTestError extends AssertionFailedError implements SkippedTest -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use SebastianBergmann\Comparator\ComparisonFailure; - -/** - * Exception for expectations which failed their check. - * - * The exception contains the error message and optionally a - * SebastianBergmann\Comparator\ComparisonFailure which is used to - * generate diff output of the failed expectations. - */ -class ExpectationFailedException extends AssertionFailedError -{ - /** - * @var ComparisonFailure - */ - protected $comparisonFailure; - - public function __construct(string $message, ComparisonFailure $comparisonFailure = null, \Exception $previous = null) - { - $this->comparisonFailure = $comparisonFailure; - - parent::__construct($message, 0, $previous); - } - - public function getComparisonFailure(): ?ComparisonFailure - { - return $this->comparisonFailure; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -use PHPUnit\Framework\Error\Error; -use Throwable; - -/** - * A TestFailure collects a failed test together with the caught exception. - */ -class TestFailure -{ - /** - * @var null|Test - */ - protected $failedTest; - - /** - * @var Throwable - */ - protected $thrownException; - - /** - * @var string - */ - private $testName; - - /** - * Returns a description for an exception. - * - * @throws \InvalidArgumentException - */ - public static function exceptionToString(Throwable $e): string - { - if ($e instanceof SelfDescribing) { - $buffer = $e->toString(); - - if ($e instanceof ExpectationFailedException && $e->getComparisonFailure()) { - $buffer .= $e->getComparisonFailure()->getDiff(); - } - - if (!empty($buffer)) { - $buffer = \trim($buffer) . "\n"; - } - - return $buffer; - } - - if ($e instanceof Error) { - return $e->getMessage() . "\n"; - } - - if ($e instanceof ExceptionWrapper) { - return $e->getClassName() . ': ' . $e->getMessage() . "\n"; - } - - return \get_class($e) . ': ' . $e->getMessage() . "\n"; - } - - /** - * Constructs a TestFailure with the given test and exception. - * - * @param Throwable $t - */ - public function __construct(Test $failedTest, $t) - { - if ($failedTest instanceof SelfDescribing) { - $this->testName = $failedTest->toString(); - } else { - $this->testName = \get_class($failedTest); - } - - if (!$failedTest instanceof TestCase || !$failedTest->isInIsolation()) { - $this->failedTest = $failedTest; - } - - $this->thrownException = $t; - } - - /** - * Returns a short description of the failure. - */ - public function toString(): string - { - return \sprintf( - '%s: %s', - $this->testName, - $this->thrownException->getMessage() - ); - } - - /** - * Returns a description for the thrown exception. - * - * @throws \InvalidArgumentException - */ - public function getExceptionAsString(): string - { - return self::exceptionToString($this->thrownException); - } - - /** - * Returns the name of the failing test (including data set, if any). - */ - public function getTestName(): string - { - return $this->testName; - } - - /** - * Returns the failing test. - * - * Note: The test object is not set when the test is executed in process - * isolation. - * - * @see Exception - */ - public function failedTest(): ?Test - { - return $this->failedTest; - } - - /** - * Gets the thrown exception. - */ - public function thrownException(): Throwable - { - return $this->thrownException; - } - - /** - * Returns the exception's message. - */ - public function exceptionMessage(): string - { - return $this->thrownException()->getMessage(); - } - - /** - * Returns true if the thrown exception - * is of type AssertionFailedError. - */ - public function isFailure(): bool - { - return $this->thrownException() instanceof AssertionFailedError; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Framework; - -/** - * Extension to PHPUnit\Framework\AssertionFailedError to mark the special - * case of a test that unintentionally covers code. - */ -class UnintentionallyCoveredCodeError extends RiskyTestError -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use DOMCharacterData; -use DOMDocument; -use DOMElement; -use DOMNode; -use DOMText; -use PHPUnit\Framework\Exception; -use ReflectionClass; - -final class Xml -{ - public static function import(DOMElement $element): DOMElement - { - $document = new DOMDocument; - - return $document->importNode($element, true); - } - - /** - * Load an $actual document into a DOMDocument. This is called - * from the selector assertions. - * - * If $actual is already a DOMDocument, it is returned with - * no changes. Otherwise, $actual is loaded into a new DOMDocument - * as either HTML or XML, depending on the value of $isHtml. If $isHtml is - * false and $xinclude is true, xinclude is performed on the loaded - * DOMDocument. - * - * Note: prior to PHPUnit 3.3.0, this method loaded a file and - * not a string as it currently does. To load a file into a - * DOMDocument, use loadFile() instead. - * - * @param DOMDocument|string $actual - * - * @throws Exception - */ - public static function load($actual, bool $isHtml = false, string $filename = '', bool $xinclude = false, bool $strict = false): DOMDocument - { - if ($actual instanceof DOMDocument) { - return $actual; - } - - if (!\is_string($actual)) { - throw new Exception('Could not load XML from ' . \gettype($actual)); - } - - if ($actual === '') { - throw new Exception('Could not load XML from empty string'); - } - - // Required for XInclude on Windows. - if ($xinclude) { - $cwd = \getcwd(); - @\chdir(\dirname($filename)); - } - - $document = new DOMDocument; - $document->preserveWhiteSpace = false; - - $internal = \libxml_use_internal_errors(true); - $message = ''; - $reporting = \error_reporting(0); - - if ($filename !== '') { - // Required for XInclude - $document->documentURI = $filename; - } - - if ($isHtml) { - $loaded = $document->loadHTML($actual); - } else { - $loaded = $document->loadXML($actual); - } - - if (!$isHtml && $xinclude) { - $document->xinclude(); - } - - foreach (\libxml_get_errors() as $error) { - $message .= "\n" . $error->message; - } - - \libxml_use_internal_errors($internal); - \error_reporting($reporting); - - if (isset($cwd)) { - @\chdir($cwd); - } - - if ($loaded === false || ($strict && $message !== '')) { - if ($filename !== '') { - throw new Exception( - \sprintf( - 'Could not load "%s".%s', - $filename, - $message !== '' ? "\n" . $message : '' - ) - ); - } - - if ($message === '') { - $message = 'Could not load XML for unknown reason'; - } - - throw new Exception($message); - } - - return $document; - } - - /** - * Loads an XML (or HTML) file into a DOMDocument object. - * - * @throws Exception - */ - public static function loadFile(string $filename, bool $isHtml = false, bool $xinclude = false, bool $strict = false): DOMDocument - { - $reporting = \error_reporting(0); - $contents = \file_get_contents($filename); - - \error_reporting($reporting); - - if ($contents === false) { - throw new Exception( - \sprintf( - 'Could not read "%s".', - $filename - ) - ); - } - - return self::load($contents, $isHtml, $filename, $xinclude, $strict); - } - - public static function removeCharacterDataNodes(DOMNode $node): void - { - if ($node->hasChildNodes()) { - for ($i = $node->childNodes->length - 1; $i >= 0; $i--) { - if (($child = $node->childNodes->item($i)) instanceof DOMCharacterData) { - $node->removeChild($child); - } - } - } - } - - /** - * Escapes a string for the use in XML documents - * - * Any Unicode character is allowed, excluding the surrogate blocks, FFFE, - * and FFFF (not even as character reference). - * - * @see https://www.w3.org/TR/xml/#charsets - */ - public static function prepareString(string $string): string - { - return \preg_replace( - '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]/', - '', - \htmlspecialchars( - self::convertToUtf8($string), - \ENT_QUOTES - ) - ); - } - - /** - * "Convert" a DOMElement object into a PHP variable. - */ - public static function xmlToVariable(DOMElement $element) - { - $variable = null; - - switch ($element->tagName) { - case 'array': - $variable = []; - - foreach ($element->childNodes as $entry) { - if (!$entry instanceof DOMElement || $entry->tagName !== 'element') { - continue; - } - $item = $entry->childNodes->item(0); - - if ($item instanceof DOMText) { - $item = $entry->childNodes->item(1); - } - - $value = self::xmlToVariable($item); - - if ($entry->hasAttribute('key')) { - $variable[(string) $entry->getAttribute('key')] = $value; - } else { - $variable[] = $value; - } - } - - break; - - case 'object': - $className = $element->getAttribute('class'); - - if ($element->hasChildNodes()) { - $arguments = $element->childNodes->item(0)->childNodes; - $constructorArgs = []; - - foreach ($arguments as $argument) { - if ($argument instanceof DOMElement) { - $constructorArgs[] = self::xmlToVariable($argument); - } - } - - $class = new ReflectionClass($className); - $variable = $class->newInstanceArgs($constructorArgs); - } else { - $variable = new $className; - } - - break; - - case 'boolean': - $variable = $element->textContent === 'true'; - - break; - - case 'integer': - case 'double': - case 'string': - $variable = $element->textContent; - - \settype($variable, $element->tagName); - - break; - } - - return $variable; - } - - private static function convertToUtf8(string $string): string - { - if (!self::isUtf8($string)) { - $string = \mb_convert_encoding($string, 'UTF-8'); - } - - return $string; - } - - private static function isUtf8(string $string): bool - { - $length = \strlen($string); - - for ($i = 0; $i < $length; $i++) { - if (\ord($string[$i]) < 0x80) { - $n = 0; - } elseif ((\ord($string[$i]) & 0xE0) === 0xC0) { - $n = 1; - } elseif ((\ord($string[$i]) & 0xF0) === 0xE0) { - $n = 2; - } elseif ((\ord($string[$i]) & 0xF0) === 0xF0) { - $n = 3; - } else { - return false; - } - - for ($j = 0; $j < $n; $j++) { - if ((++$i === $length) || ((\ord($string[$i]) & 0xC0) !== 0x80)) { - return false; - } - } - } - - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -final class ConfigurationGenerator -{ - /** - * @var string - */ - private const TEMPLATE = << - - - - {tests_directory} - - - - - - {src_directory} - - - - -EOT; - - public function generateDefaultConfiguration(string $phpunitVersion, string $bootstrapScript, string $testsDirectory, string $srcDirectory): string - { - return \str_replace( - [ - '{phpunit_version}', - '{bootstrap_script}', - '{tests_directory}', - '{src_directory}', - ], - [ - $phpunitVersion, - $bootstrapScript, - $testsDirectory, - $srcDirectory, - ], - self::TEMPLATE - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use Closure; - -final class GlobalState -{ - /** - * @var string[] - */ - private const SUPER_GLOBAL_ARRAYS = [ - '_ENV', - '_POST', - '_GET', - '_COOKIE', - '_SERVER', - '_FILES', - '_REQUEST', - ]; - - public static function getIncludedFilesAsString(): string - { - return static::processIncludedFilesAsString(\get_included_files()); - } - - /** - * @param string[] $files - */ - public static function processIncludedFilesAsString(array $files): string - { - $blacklist = new Blacklist; - $prefix = false; - $result = ''; - - if (\defined('__PHPUNIT_PHAR__')) { - $prefix = 'phar://' . __PHPUNIT_PHAR__ . '/'; - } - - for ($i = \count($files) - 1; $i > 0; $i--) { - $file = $files[$i]; - - if (!empty($GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST']) && - \in_array($file, $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'])) { - continue; - } - - if ($prefix !== false && \strpos($file, $prefix) === 0) { - continue; - } - - // Skip virtual file system protocols - if (\preg_match('/^(vfs|phpvfs[a-z0-9]+):/', $file)) { - continue; - } - - if (!$blacklist->isBlacklisted($file) && \is_file($file)) { - $result = 'require_once \'' . $file . "';\n" . $result; - } - } - - return $result; - } - - public static function getIniSettingsAsString(): string - { - $result = ''; - $iniSettings = \ini_get_all(null, false); - - foreach ($iniSettings as $key => $value) { - $result .= \sprintf( - '@ini_set(%s, %s);' . "\n", - self::exportVariable($key), - self::exportVariable($value) - ); - } - - return $result; - } - - public static function getConstantsAsString(): string - { - $constants = \get_defined_constants(true); - $result = ''; - - if (isset($constants['user'])) { - foreach ($constants['user'] as $name => $value) { - $result .= \sprintf( - 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", - $name, - $name, - self::exportVariable($value) - ); - } - } - - return $result; - } - - public static function getGlobalsAsString(): string - { - $result = ''; - - foreach (self::SUPER_GLOBAL_ARRAYS as $superGlobalArray) { - if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { - foreach (\array_keys($GLOBALS[$superGlobalArray]) as $key) { - if ($GLOBALS[$superGlobalArray][$key] instanceof Closure) { - continue; - } - - $result .= \sprintf( - '$GLOBALS[\'%s\'][\'%s\'] = %s;' . "\n", - $superGlobalArray, - $key, - self::exportVariable($GLOBALS[$superGlobalArray][$key]) - ); - } - } - } - - $blacklist = self::SUPER_GLOBAL_ARRAYS; - $blacklist[] = 'GLOBALS'; - - foreach (\array_keys($GLOBALS) as $key) { - if (!$GLOBALS[$key] instanceof Closure && !\in_array($key, $blacklist, true)) { - $result .= \sprintf( - '$GLOBALS[\'%s\'] = %s;' . "\n", - $key, - self::exportVariable($GLOBALS[$key]) - ); - } - } - - return $result; - } - - private static function exportVariable($variable): string - { - if (\is_scalar($variable) || $variable === null || - (\is_array($variable) && self::arrayOnlyContainsScalars($variable))) { - return \var_export($variable, true); - } - - return 'unserialize(' . \var_export(\serialize($variable), true) . ')'; - } - - private static function arrayOnlyContainsScalars(array $array): bool - { - $result = true; - - foreach ($array as $element) { - if (\is_array($element)) { - $result = self::arrayOnlyContainsScalars($element); - } elseif (!\is_scalar($element) && $element !== null) { - $result = false; - } - - if ($result === false) { - break; - } - } - - return $result; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use __PHP_Incomplete_Class; -use ErrorException; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\SyntheticError; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; -use PHPUnit\Framework\TestResult; -use SebastianBergmann\Environment\Runtime; - -/** - * Utility methods for PHP sub-processes. - */ -abstract class AbstractPhpProcess -{ - /** - * @var Runtime - */ - protected $runtime; - - /** - * @var bool - */ - protected $stderrRedirection = false; - - /** - * @var string - */ - protected $stdin = ''; - - /** - * @var string - */ - protected $args = ''; - - /** - * @var array - */ - protected $env = []; - - /** - * @var int - */ - protected $timeout = 0; - - public static function factory(): self - { - if (\DIRECTORY_SEPARATOR === '\\') { - return new WindowsPhpProcess; - } - - return new DefaultPhpProcess; - } - - public function __construct() - { - $this->runtime = new Runtime; - } - - /** - * Defines if should use STDERR redirection or not. - * - * Then $stderrRedirection is TRUE, STDERR is redirected to STDOUT. - */ - public function setUseStderrRedirection(bool $stderrRedirection): void - { - $this->stderrRedirection = $stderrRedirection; - } - - /** - * Returns TRUE if uses STDERR redirection or FALSE if not. - */ - public function useStderrRedirection(): bool - { - return $this->stderrRedirection; - } - - /** - * Sets the input string to be sent via STDIN - */ - public function setStdin(string $stdin): void - { - $this->stdin = $stdin; - } - - /** - * Returns the input string to be sent via STDIN - */ - public function getStdin(): string - { - return $this->stdin; - } - - /** - * Sets the string of arguments to pass to the php job - */ - public function setArgs(string $args): void - { - $this->args = $args; - } - - /** - * Returns the string of arguments to pass to the php job - */ - public function getArgs(): string - { - return $this->args; - } - - /** - * Sets the array of environment variables to start the child process with - * - * @param array $env - */ - public function setEnv(array $env): void - { - $this->env = $env; - } - - /** - * Returns the array of environment variables to start the child process with - */ - public function getEnv(): array - { - return $this->env; - } - - /** - * Sets the amount of seconds to wait before timing out - */ - public function setTimeout(int $timeout): void - { - $this->timeout = $timeout; - } - - /** - * Returns the amount of seconds to wait before timing out - */ - public function getTimeout(): int - { - return $this->timeout; - } - - /** - * Runs a single test in a separate PHP process. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function runTestJob(string $job, Test $test, TestResult $result): void - { - $result->startTest($test); - - $_result = $this->runJob($job); - - $this->processChildResult( - $test, - $result, - $_result['stdout'], - $_result['stderr'] - ); - } - - /** - * Returns the command based into the configurations. - */ - public function getCommand(array $settings, string $file = null): string - { - $command = $this->runtime->getBinary(); - $command .= $this->settingsToParameters($settings); - - if (\PHP_SAPI === 'phpdbg') { - $command .= ' -qrr'; - - if (!$file) { - $command .= 's='; - } - } - - if ($file) { - $command .= ' ' . \escapeshellarg($file); - } - - if ($this->args) { - if (!$file) { - $command .= ' --'; - } - $command .= ' ' . $this->args; - } - - if ($this->stderrRedirection === true) { - $command .= ' 2>&1'; - } - - return $command; - } - - /** - * Runs a single job (PHP code) using a separate PHP process. - */ - abstract public function runJob(string $job, array $settings = []): array; - - protected function settingsToParameters(array $settings): string - { - $buffer = ''; - - foreach ($settings as $setting) { - $buffer .= ' -d ' . \escapeshellarg($setting); - } - - return $buffer; - } - - /** - * Processes the TestResult object from an isolated process. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - private function processChildResult(Test $test, TestResult $result, string $stdout, string $stderr): void - { - $time = 0; - - if (!empty($stderr)) { - $result->addError( - $test, - new Exception(\trim($stderr)), - $time - ); - } else { - \set_error_handler(function ($errno, $errstr, $errfile, $errline): void { - throw new ErrorException($errstr, $errno, $errno, $errfile, $errline); - }); - - try { - if (\strpos($stdout, "#!/usr/bin/env php\n") === 0) { - $stdout = \substr($stdout, 19); - } - - $childResult = \unserialize(\str_replace("#!/usr/bin/env php\n", '', $stdout)); - \restore_error_handler(); - } catch (ErrorException $e) { - \restore_error_handler(); - $childResult = false; - - $result->addError( - $test, - new Exception(\trim($stdout), 0, $e), - $time - ); - } - - if ($childResult !== false) { - if (!empty($childResult['output'])) { - $output = $childResult['output']; - } - - /* @var TestCase $test */ - - $test->setResult($childResult['testResult']); - $test->addToAssertionCount($childResult['numAssertions']); - - /** @var TestResult $childResult */ - $childResult = $childResult['result']; - - if ($result->getCollectCodeCoverageInformation()) { - $result->getCodeCoverage()->merge( - $childResult->getCodeCoverage() - ); - } - - $time = $childResult->time(); - $notImplemented = $childResult->notImplemented(); - $risky = $childResult->risky(); - $skipped = $childResult->skipped(); - $errors = $childResult->errors(); - $warnings = $childResult->warnings(); - $failures = $childResult->failures(); - - if (!empty($notImplemented)) { - $result->addError( - $test, - $this->getException($notImplemented[0]), - $time - ); - } elseif (!empty($risky)) { - $result->addError( - $test, - $this->getException($risky[0]), - $time - ); - } elseif (!empty($skipped)) { - $result->addError( - $test, - $this->getException($skipped[0]), - $time - ); - } elseif (!empty($errors)) { - $result->addError( - $test, - $this->getException($errors[0]), - $time - ); - } elseif (!empty($warnings)) { - $result->addWarning( - $test, - $this->getException($warnings[0]), - $time - ); - } elseif (!empty($failures)) { - $result->addFailure( - $test, - $this->getException($failures[0]), - $time - ); - } - } - } - - $result->endTest($test, $time); - - if (!empty($output)) { - print $output; - } - } - - /** - * Gets the thrown exception from a PHPUnit\Framework\TestFailure. - * - * @see https://github.com/sebastianbergmann/phpunit/issues/74 - */ - private function getException(TestFailure $error): Exception - { - $exception = $error->thrownException(); - - if ($exception instanceof __PHP_Incomplete_Class) { - $exceptionArray = []; - - foreach ((array) $exception as $key => $value) { - $key = \substr($key, \strrpos($key, "\0") + 1); - $exceptionArray[$key] = $value; - } - - $exception = new SyntheticError( - \sprintf( - '%s: %s', - $exceptionArray['_PHP_Incomplete_Class_Name'], - $exceptionArray['message'] - ), - $exceptionArray['code'], - $exceptionArray['file'], - $exceptionArray['line'], - $exceptionArray['trace'] - ); - } - - return $exception; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use PHPUnit\Framework\Exception; - -/** - * Windows utility for PHP sub-processes. - * - * Reading from STDOUT or STDERR hangs forever on Windows if the output is - * too large. - * - * @see https://bugs.php.net/bug.php?id=51800 - */ -class WindowsPhpProcess extends DefaultPhpProcess -{ - public function getCommand(array $settings, string $file = null): string - { - return '"' . parent::getCommand($settings, $file) . '"'; - } - - protected function getHandles(): array - { - if (false === $stdout_handle = \tmpfile()) { - throw new Exception( - 'A temporary file could not be created; verify that your TEMP environment variable is writable' - ); - } - - return [ - 1 => $stdout_handle, - ]; - } - - protected function useTemporaryFile(): bool - { - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\PHP; - -use PHPUnit\Framework\Exception; - -/** - * Default utility for PHP sub-processes. - */ -class DefaultPhpProcess extends AbstractPhpProcess -{ - /** - * @var string - */ - protected $tempFile; - - /** - * Runs a single job (PHP code) using a separate PHP process. - * - * @throws Exception - */ - public function runJob(string $job, array $settings = []): array - { - if ($this->useTemporaryFile() || $this->stdin) { - if (!($this->tempFile = \tempnam(\sys_get_temp_dir(), 'PHPUnit')) || - \file_put_contents($this->tempFile, $job) === false) { - throw new Exception( - 'Unable to write temporary file' - ); - } - - $job = $this->stdin; - } - - return $this->runProcess($job, $settings); - } - - /** - * Returns an array of file handles to be used in place of pipes - */ - protected function getHandles(): array - { - return []; - } - - /** - * Handles creating the child process and returning the STDOUT and STDERR - * - * @throws Exception - */ - protected function runProcess(string $job, array $settings): array - { - $handles = $this->getHandles(); - - $env = null; - - if ($this->env) { - $env = $_SERVER ?? []; - unset($env['argv'], $env['argc']); - $env = \array_merge($env, $this->env); - - foreach ($env as $envKey => $envVar) { - if (\is_array($envVar)) { - unset($env[$envKey]); - } - } - } - - $pipeSpec = [ - 0 => $handles[0] ?? ['pipe', 'r'], - 1 => $handles[1] ?? ['pipe', 'w'], - 2 => $handles[2] ?? ['pipe', 'w'], - ]; - - $process = \proc_open( - $this->getCommand($settings, $this->tempFile), - $pipeSpec, - $pipes, - null, - $env - ); - - if (!\is_resource($process)) { - throw new Exception( - 'Unable to spawn worker process' - ); - } - - if ($job) { - $this->process($pipes[0], $job); - } - - \fclose($pipes[0]); - - $stderr = $stdout = ''; - - if ($this->timeout) { - unset($pipes[0]); - - while (true) { - $r = $pipes; - $w = null; - $e = null; - - $n = @\stream_select($r, $w, $e, $this->timeout); - - if ($n === false) { - break; - } - - if ($n === 0) { - \proc_terminate($process, 9); - - throw new Exception( - \sprintf( - 'Job execution aborted after %d seconds', - $this->timeout - ) - ); - } - - if ($n > 0) { - foreach ($r as $pipe) { - $pipeOffset = 0; - - foreach ($pipes as $i => $origPipe) { - if ($pipe === $origPipe) { - $pipeOffset = $i; - - break; - } - } - - if (!$pipeOffset) { - break; - } - - $line = \fread($pipe, 8192); - - if ($line === '') { - \fclose($pipes[$pipeOffset]); - - unset($pipes[$pipeOffset]); - } else { - if ($pipeOffset === 1) { - $stdout .= $line; - } else { - $stderr .= $line; - } - } - } - - if (empty($pipes)) { - break; - } - } - } - } else { - if (isset($pipes[1])) { - $stdout = \stream_get_contents($pipes[1]); - - \fclose($pipes[1]); - } - - if (isset($pipes[2])) { - $stderr = \stream_get_contents($pipes[2]); - - \fclose($pipes[2]); - } - } - - if (isset($handles[1])) { - \rewind($handles[1]); - - $stdout = \stream_get_contents($handles[1]); - - \fclose($handles[1]); - } - - if (isset($handles[2])) { - \rewind($handles[2]); - - $stderr = \stream_get_contents($handles[2]); - - \fclose($handles[2]); - } - - \proc_close($process); - - $this->cleanup(); - - return ['stdout' => $stdout, 'stderr' => $stderr]; - } - - protected function process($pipe, string $job): void - { - \fwrite($pipe, $job); - } - - protected function cleanup(): void - { - if ($this->tempFile) { - \unlink($this->tempFile); - } - } - - protected function useTemporaryFile(): bool - { - return false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -eval('?>' . \file_get_contents('php://stdin')); -setCodeCoverage( - new CodeCoverage( - null, - unserialize('{codeCoverageFilter}') - ) - ); - } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - $test = new {className}('{name}', unserialize('{data}'), '{dataName}'); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(TRUE); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); - } - - ini_set('xdebug.scream', 0); - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); - } - } - - print serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = PHPUnit\Util\Configuration::getInstance($configurationFilePath); - $configuration->handlePHPConfiguration(); - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); -} - -__phpunit_run_isolated_test(); -setCodeCoverage( - new CodeCoverage( - null, - unserialize('{codeCoverageFilter}') - ) - ); - } - - $result->beStrictAboutTestsThatDoNotTestAnything({isStrictAboutTestsThatDoNotTestAnything}); - $result->beStrictAboutOutputDuringTests({isStrictAboutOutputDuringTests}); - $result->enforceTimeLimit({enforcesTimeLimit}); - $result->beStrictAboutTodoAnnotatedTests({isStrictAboutTodoAnnotatedTests}); - $result->beStrictAboutResourceUsageDuringSmallTests({isStrictAboutResourceUsageDuringSmallTests}); - - /** @var TestCase $test */ - $test = new {className}('{methodName}', unserialize('{data}'), '{dataName}'); - $test->setDependencyInput(unserialize('{dependencyInput}')); - $test->setInIsolation(TRUE); - - ob_end_clean(); - $test->run($result); - $output = ''; - if (!$test->hasExpectationOnOutput()) { - $output = $test->getActualOutput(); - } - - ini_set('xdebug.scream', '0'); - @rewind(STDOUT); /* @ as not every STDOUT target stream is rewindable */ - if ($stdout = stream_get_contents(STDOUT)) { - $output = $stdout . $output; - $streamMetaData = stream_get_meta_data(STDOUT); - if (!empty($streamMetaData['stream_type']) && 'STDIO' === $streamMetaData['stream_type']) { - @ftruncate(STDOUT, 0); - @rewind(STDOUT); - } - } - - print serialize( - [ - 'testResult' => $test->getResult(), - 'numAssertions' => $test->getNumAssertions(), - 'result' => $result, - 'output' => $output - ] - ); -} - -$configurationFilePath = '{configurationFilePath}'; - -if ('' !== $configurationFilePath) { - $configuration = PHPUnit\Util\Configuration::getInstance($configurationFilePath); - $configuration->handlePHPConfiguration(); - unset($configuration); -} - -function __phpunit_error_handler($errno, $errstr, $errfile, $errline, $errcontext) -{ - return true; -} - -set_error_handler('__phpunit_error_handler'); - -{constants} -{included_files} -{globals} - -restore_error_handler(); - -if (isset($GLOBALS['__PHPUNIT_BOOTSTRAP'])) { - require_once $GLOBALS['__PHPUNIT_BOOTSTRAP']; - unset($GLOBALS['__PHPUNIT_BOOTSTRAP']); -} - -__phpunit_run_isolated_test(); -start(__FILE__); -} - -register_shutdown_function(function() use ($coverage) { - $output = null; - if ($coverage) { - $output = $coverage->stop(); - } - file_put_contents('{coverageFile}', serialize($output)); -}); - -ob_end_clean(); - -require '{job}'; - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -final class RegularExpression -{ - /** - * @throws \Exception - * - * @return false|int - */ - public static function safeMatch(string $pattern, string $subject, ?array $matches = null, int $flags = 0, int $offset = 0) - { - $handler_terminator = ErrorHandler::handleErrorOnce(); - $match = \preg_match($pattern, $subject, $matches, $flags, $offset); - $handler_terminator(); - - return $match; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PharIo\Version\VersionConstraintParser; -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\CodeCoverageException; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\InvalidCoversTargetException; -use PHPUnit\Framework\SelfDescribing; -use PHPUnit\Framework\SkippedTestError; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\Warning; -use PHPUnit\Runner\Version; -use ReflectionClass; -use ReflectionException; -use ReflectionFunction; -use ReflectionMethod; -use SebastianBergmann\Environment\OperatingSystem; -use Traversable; - -final class Test -{ - /** - * @var int - */ - public const UNKNOWN = -1; - - /** - * @var int - */ - public const SMALL = 0; - - /** - * @var int - */ - public const MEDIUM = 1; - - /** - * @var int - */ - public const LARGE = 2; - - /** - * @var string - * - * @todo This constant should be private (it's public because of TestTest::testGetProvidedDataRegEx) - */ - public const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/'; - - /** - * @var string - */ - private const REGEX_TEST_WITH = '/@testWith\s+/'; - - /** - * @var string - */ - private const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m'; - - /** - * @var string - */ - private const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; - - /** - * @var string - */ - private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\t -.|~^]+)[ \t]*\r?$/m'; - - /** - * @var string - */ - private const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m'; - - /** - * @var string - */ - private const REGEX_REQUIRES_SETTING = '/@requires\s+(?Psetting)\s+(?P([^ ]+?))\s*(?P[\w\.-]+[\w\.]?)?[ \t]*\r?$/m'; - - /** - * @var string - */ - private const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^\s<>=!]+))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; - - /** - * @var array - */ - private static $annotationCache = []; - - /** - * @var array - */ - private static $hookMethods = []; - - public static function describe(\PHPUnit\Framework\Test $test): array - { - if ($test instanceof TestCase) { - return [\get_class($test), $test->getName()]; - } - - if ($test instanceof SelfDescribing) { - return ['', $test->toString()]; - } - - return ['', \get_class($test)]; - } - - public static function describeAsString(\PHPUnit\Framework\Test $test): string - { - if ($test instanceof SelfDescribing) { - return $test->toString(); - } - - return \get_class($test); - } - - /** - * @throws CodeCoverageException - * - * @return array|bool - */ - public static function getLinesToBeCovered(string $className, string $methodName) - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - if (self::shouldCoversAnnotationBeUsed($annotations) === false) { - return false; - } - - return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers'); - } - - /** - * Returns lines of code specified with the @uses annotation. - * - * @throws CodeCoverageException - */ - public static function getLinesToBeUsed(string $className, string $methodName): array - { - return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses'); - } - - /** - * Returns the requirements for a test. - * - * @throws Warning - */ - public static function getRequirements(string $className, string $methodName): array - { - $reflector = new ReflectionClass($className); - $docComment = $reflector->getDocComment(); - $reflector = new ReflectionMethod($className, $methodName); - $docComment .= "\n" . $reflector->getDocComment(); - $requires = []; - - if ($count = \preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) { - foreach (\range(0, $count - 1) as $i) { - $requires[$matches['name'][$i]] = $matches['value'][$i]; - } - } - - if ($count = \preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) { - foreach (\range(0, $count - 1) as $i) { - $requires[$matches['name'][$i]] = [ - 'version' => $matches['version'][$i], - 'operator' => $matches['operator'][$i], - ]; - } - } - - if ($count = \preg_match_all(self::REGEX_REQUIRES_VERSION_CONSTRAINT, $docComment, $matches)) { - foreach (\range(0, $count - 1) as $i) { - if (!empty($requires[$matches['name'][$i]])) { - continue; - } - - try { - $versionConstraintParser = new VersionConstraintParser; - - $requires[$matches['name'][$i] . '_constraint'] = [ - 'constraint' => $versionConstraintParser->parse(\trim($matches['constraint'][$i])), - ]; - } catch (\PharIo\Version\Exception $e) { - throw new Warning($e->getMessage(), $e->getCode(), $e); - } - } - } - - if ($count = \preg_match_all(self::REGEX_REQUIRES_SETTING, $docComment, $matches)) { - $requires['setting'] = []; - - foreach (\range(0, $count - 1) as $i) { - $requires['setting'][$matches['setting'][$i]] = $matches['value'][$i]; - } - } - - if ($count = \preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) { - foreach (\range(0, $count - 1) as $i) { - $name = $matches['name'][$i] . 's'; - - if (!isset($requires[$name])) { - $requires[$name] = []; - } - - $requires[$name][] = $matches['value'][$i]; - - if ($name !== 'extensions' || empty($matches['version'][$i])) { - continue; - } - - $requires['extension_versions'][$matches['value'][$i]] = [ - 'version' => $matches['version'][$i], - 'operator' => $matches['operator'][$i], - ]; - } - } - - return $requires; - } - - /** - * Returns the missing requirements for a test. - * - * @throws Warning - * - * @return string[] - */ - public static function getMissingRequirements(string $className, string $methodName): array - { - $required = static::getRequirements($className, $methodName); - $missing = []; - - if (!empty($required['PHP'])) { - $operator = empty($required['PHP']['operator']) ? '>=' : $required['PHP']['operator']; - - if (!\version_compare(\PHP_VERSION, $required['PHP']['version'], $operator)) { - $missing[] = \sprintf('PHP %s %s is required.', $operator, $required['PHP']['version']); - } - } elseif (!empty($required['PHP_constraint'])) { - $version = new \PharIo\Version\Version(self::sanitizeVersionNumber(\PHP_VERSION)); - - if (!$required['PHP_constraint']['constraint']->complies($version)) { - $missing[] = \sprintf( - 'PHP version does not match the required constraint %s.', - $required['PHP_constraint']['constraint']->asString() - ); - } - } - - if (!empty($required['PHPUnit'])) { - $phpunitVersion = Version::id(); - - $operator = empty($required['PHPUnit']['operator']) ? '>=' : $required['PHPUnit']['operator']; - - if (!\version_compare($phpunitVersion, $required['PHPUnit']['version'], $operator)) { - $missing[] = \sprintf('PHPUnit %s %s is required.', $operator, $required['PHPUnit']['version']); - } - } elseif (!empty($required['PHPUnit_constraint'])) { - $phpunitVersion = new \PharIo\Version\Version(self::sanitizeVersionNumber(Version::id())); - - if (!$required['PHPUnit_constraint']['constraint']->complies($phpunitVersion)) { - $missing[] = \sprintf( - 'PHPUnit version does not match the required constraint %s.', - $required['PHPUnit_constraint']['constraint']->asString() - ); - } - } - - if (!empty($required['OSFAMILY']) && $required['OSFAMILY'] !== (new OperatingSystem)->getFamily()) { - $missing[] = \sprintf('Operating system %s is required.', $required['OSFAMILY']); - } - - if (!empty($required['OS'])) { - $requiredOsPattern = \sprintf('/%s/i', \addcslashes($required['OS'], '/')); - - if (!\preg_match($requiredOsPattern, \PHP_OS)) { - $missing[] = \sprintf('Operating system matching %s is required.', $requiredOsPattern); - } - } - - if (!empty($required['functions'])) { - foreach ($required['functions'] as $function) { - $pieces = \explode('::', $function); - - if (\count($pieces) === 2 && \method_exists($pieces[0], $pieces[1])) { - continue; - } - - if (\function_exists($function)) { - continue; - } - - $missing[] = \sprintf('Function %s is required.', $function); - } - } - - if (!empty($required['setting'])) { - foreach ($required['setting'] as $setting => $value) { - if (\ini_get($setting) != $value) { - $missing[] = \sprintf('Setting "%s" must be "%s".', $setting, $value); - } - } - } - - if (!empty($required['extensions'])) { - foreach ($required['extensions'] as $extension) { - if (isset($required['extension_versions'][$extension])) { - continue; - } - - if (!\extension_loaded($extension)) { - $missing[] = \sprintf('Extension %s is required.', $extension); - } - } - } - - if (!empty($required['extension_versions'])) { - foreach ($required['extension_versions'] as $extension => $required) { - $actualVersion = \phpversion($extension); - - $operator = empty($required['operator']) ? '>=' : $required['operator']; - - if ($actualVersion === false || !\version_compare($actualVersion, $required['version'], $operator)) { - $missing[] = \sprintf('Extension %s %s %s is required.', $extension, $operator, $required['version']); - } - } - } - - return $missing; - } - - /** - * Returns the expected exception for a test. - * - * @return array|false - */ - public static function getExpectedException(string $className, ?string $methodName) - { - $reflector = new ReflectionMethod($className, $methodName); - $docComment = $reflector->getDocComment(); - $docComment = \substr($docComment, 3, -2); - - if (\preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - $class = $matches[1]; - $code = null; - $message = ''; - $messageRegExp = ''; - - if (isset($matches[2])) { - $message = \trim($matches[2]); - } elseif (isset($annotations['method']['expectedExceptionMessage'])) { - $message = self::parseAnnotationContent( - $annotations['method']['expectedExceptionMessage'][0] - ); - } - - if (isset($annotations['method']['expectedExceptionMessageRegExp'])) { - $messageRegExp = self::parseAnnotationContent( - $annotations['method']['expectedExceptionMessageRegExp'][0] - ); - } - - if (isset($matches[3])) { - $code = $matches[3]; - } elseif (isset($annotations['method']['expectedExceptionCode'])) { - $code = self::parseAnnotationContent( - $annotations['method']['expectedExceptionCode'][0] - ); - } - - if (\is_numeric($code)) { - $code = (int) $code; - } elseif (\is_string($code) && \defined($code)) { - $code = (int) \constant($code); - } - - return [ - 'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp, - ]; - } - - return false; - } - - /** - * Returns the provided data for a method. - * - * @throws Exception - */ - public static function getProvidedData(string $className, string $methodName): ?array - { - $reflector = new ReflectionMethod($className, $methodName); - $docComment = $reflector->getDocComment(); - - $data = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName); - - if ($data === null) { - $data = self::getDataFromTestWithAnnotation($docComment); - } - - if ($data === []) { - throw new SkippedTestError; - } - - if ($data !== null) { - foreach ($data as $key => $value) { - if (!\is_array($value)) { - throw new Exception( - \sprintf( - 'Data set %s is invalid.', - \is_int($key) ? '#' . $key : '"' . $key . '"' - ) - ); - } - } - } - - return $data; - } - - /** - * @throws Exception - */ - public static function getDataFromTestWithAnnotation(string $docComment): ?array - { - $docComment = self::cleanUpMultiLineAnnotation($docComment); - - if (\preg_match(self::REGEX_TEST_WITH, $docComment, $matches, \PREG_OFFSET_CAPTURE)) { - $offset = \strlen($matches[0][0]) + $matches[0][1]; - $annotationContent = \substr($docComment, $offset); - $data = []; - - foreach (\explode("\n", $annotationContent) as $candidateRow) { - $candidateRow = \trim($candidateRow); - - if ($candidateRow[0] !== '[') { - break; - } - - $dataSet = \json_decode($candidateRow, true); - - if (\json_last_error() !== \JSON_ERROR_NONE) { - throw new Exception( - 'The data set for the @testWith annotation cannot be parsed: ' . \json_last_error_msg() - ); - } - - $data[] = $dataSet; - } - - if (!$data) { - throw new Exception('The data set for the @testWith annotation cannot be parsed.'); - } - - return $data; - } - - return null; - } - - public static function parseTestMethodAnnotations(string $className, ?string $methodName = ''): array - { - if (!isset(self::$annotationCache[$className])) { - $class = new ReflectionClass($className); - $traits = $class->getTraits(); - $annotations = []; - - foreach ($traits as $trait) { - $annotations = \array_merge( - $annotations, - self::parseAnnotations($trait->getDocComment()) - ); - } - - self::$annotationCache[$className] = \array_merge( - $annotations, - self::parseAnnotations($class->getDocComment()) - ); - } - - $cacheKey = $className . '::' . $methodName; - - if ($methodName !== null && !isset(self::$annotationCache[$cacheKey])) { - try { - $method = new ReflectionMethod($className, $methodName); - $annotations = self::parseAnnotations($method->getDocComment()); - } catch (ReflectionException $e) { - $annotations = []; - } - - self::$annotationCache[$cacheKey] = $annotations; - } - - return [ - 'class' => self::$annotationCache[$className], - 'method' => $methodName !== null ? self::$annotationCache[$cacheKey] : [], - ]; - } - - public static function getInlineAnnotations(string $className, string $methodName): array - { - $method = new ReflectionMethod($className, $methodName); - $code = \file($method->getFileName()); - $lineNumber = $method->getStartLine(); - $startLine = $method->getStartLine() - 1; - $endLine = $method->getEndLine() - 1; - $methodLines = \array_slice($code, $startLine, $endLine - $startLine + 1); - $annotations = []; - - foreach ($methodLines as $line) { - if (\preg_match('#/\*\*?\s*@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?\*/$#m', $line, $matches)) { - $annotations[\strtolower($matches['name'])] = [ - 'line' => $lineNumber, - 'value' => $matches['value'], - ]; - } - - $lineNumber++; - } - - return $annotations; - } - - public static function parseAnnotations(string $docBlock): array - { - $annotations = []; - // Strip away the docblock header and footer to ease parsing of one line annotations - $docBlock = \substr($docBlock, 3, -2); - - if (\preg_match_all('/@(?P[A-Za-z_-]+)(?:[ \t]+(?P.*?))?[ \t]*\r?$/m', $docBlock, $matches)) { - $numMatches = \count($matches[0]); - - for ($i = 0; $i < $numMatches; ++$i) { - $annotations[$matches['name'][$i]][] = (string) $matches['value'][$i]; - } - } - - return $annotations; - } - - public static function getBackupSettings(string $className, string $methodName): array - { - return [ - 'backupGlobals' => self::getBooleanAnnotationSetting( - $className, - $methodName, - 'backupGlobals' - ), - 'backupStaticAttributes' => self::getBooleanAnnotationSetting( - $className, - $methodName, - 'backupStaticAttributes' - ), - ]; - } - - public static function getDependencies(string $className, string $methodName): array - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - $dependencies = []; - - if (isset($annotations['class']['depends'])) { - $dependencies = $annotations['class']['depends']; - } - - if (isset($annotations['method']['depends'])) { - $dependencies = \array_merge( - $dependencies, - $annotations['method']['depends'] - ); - } - - return \array_unique($dependencies); - } - - public static function getErrorHandlerSettings(string $className, ?string $methodName): ?bool - { - return self::getBooleanAnnotationSetting( - $className, - $methodName, - 'errorHandler' - ); - } - - public static function getGroups(string $className, ?string $methodName = ''): array - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - $groups = []; - - if (isset($annotations['method']['author'])) { - $groups = $annotations['method']['author']; - } elseif (isset($annotations['class']['author'])) { - $groups = $annotations['class']['author']; - } - - if (isset($annotations['class']['group'])) { - $groups = \array_merge($groups, $annotations['class']['group']); - } - - if (isset($annotations['method']['group'])) { - $groups = \array_merge($groups, $annotations['method']['group']); - } - - if (isset($annotations['class']['ticket'])) { - $groups = \array_merge($groups, $annotations['class']['ticket']); - } - - if (isset($annotations['method']['ticket'])) { - $groups = \array_merge($groups, $annotations['method']['ticket']); - } - - foreach (['method', 'class'] as $element) { - foreach (['small', 'medium', 'large'] as $size) { - if (isset($annotations[$element][$size])) { - $groups[] = $size; - - break 2; - } - } - } - - return \array_unique($groups); - } - - public static function getSize(string $className, ?string $methodName): int - { - $groups = \array_flip(self::getGroups($className, $methodName)); - - if (isset($groups['large'])) { - return self::LARGE; - } - - if (isset($groups['medium'])) { - return self::MEDIUM; - } - - if (isset($groups['small'])) { - return self::SMALL; - } - - return self::UNKNOWN; - } - - public static function getProcessIsolationSettings(string $className, string $methodName): bool - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - return isset($annotations['class']['runTestsInSeparateProcesses']) || isset($annotations['method']['runInSeparateProcess']); - } - - public static function getClassProcessIsolationSettings(string $className, string $methodName): bool - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - return isset($annotations['class']['runClassInSeparateProcess']); - } - - public static function getPreserveGlobalStateSettings(string $className, string $methodName): ?bool - { - return self::getBooleanAnnotationSetting( - $className, - $methodName, - 'preserveGlobalState' - ); - } - - public static function getHookMethods(string $className): array - { - if (!\class_exists($className, false)) { - return self::emptyHookMethodsArray(); - } - - if (!isset(self::$hookMethods[$className])) { - self::$hookMethods[$className] = self::emptyHookMethodsArray(); - - try { - $class = new ReflectionClass($className); - - foreach ($class->getMethods() as $method) { - if ($method->getDeclaringClass()->getName() === Assert::class) { - continue; - } - - if ($method->getDeclaringClass()->getName() === TestCase::class) { - continue; - } - - if ($methodComment = $method->getDocComment()) { - if ($method->isStatic()) { - if (\strpos($methodComment, '@beforeClass') !== false) { - \array_unshift( - self::$hookMethods[$className]['beforeClass'], - $method->getName() - ); - } - - if (\strpos($methodComment, '@afterClass') !== false) { - self::$hookMethods[$className]['afterClass'][] = $method->getName(); - } - } - - if (\preg_match('/@before\b/', $methodComment) > 0) { - \array_unshift( - self::$hookMethods[$className]['before'], - $method->getName() - ); - } - - if (\preg_match('/@after\b/', $methodComment) > 0) { - self::$hookMethods[$className]['after'][] = $method->getName(); - } - } - } - } catch (ReflectionException $e) { - } - } - - return self::$hookMethods[$className]; - } - - /** - * @throws CodeCoverageException - */ - private static function getLinesToBeCoveredOrUsed(string $className, string $methodName, string $mode): array - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - $classShortcut = null; - - if (!empty($annotations['class'][$mode . 'DefaultClass'])) { - if (\count($annotations['class'][$mode . 'DefaultClass']) > 1) { - throw new CodeCoverageException( - \sprintf( - 'More than one @%sClass annotation in class or interface "%s".', - $mode, - $className - ) - ); - } - - $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0]; - } - - $list = []; - - if (isset($annotations['class'][$mode])) { - $list = $annotations['class'][$mode]; - } - - if (isset($annotations['method'][$mode])) { - $list = \array_merge($list, $annotations['method'][$mode]); - } - - $codeList = []; - - foreach (\array_unique($list) as $element) { - if ($classShortcut && \strncmp($element, '::', 2) === 0) { - $element = $classShortcut . $element; - } - - $element = \preg_replace('/[\s()]+$/', '', $element); - $element = \explode(' ', $element); - $element = $element[0]; - - if ($mode === 'covers' && \interface_exists($element)) { - throw new InvalidCoversTargetException( - \sprintf( - 'Trying to @cover interface "%s".', - $element - ) - ); - } - - $codeList = \array_merge( - $codeList, - self::resolveElementToReflectionObjects($element) - ); - } - - return self::resolveReflectionObjectsToLines($codeList); - } - - /** - * Parse annotation content to use constant/class constant values - * - * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME - * - * If the constant is not found the string is used as is to ensure maximum BC. - */ - private static function parseAnnotationContent(string $message): string - { - if (\defined($message) && (\strpos($message, '::') !== false && \substr_count($message, '::') + 1 === 2)) { - $message = \constant($message); - } - - return $message; - } - - /** - * Returns the provided data for a method. - */ - private static function getDataFromDataProviderAnnotation(string $docComment, string $className, string $methodName): ?iterable - { - if (\preg_match_all(self::REGEX_DATA_PROVIDER, $docComment, $matches)) { - $result = []; - - foreach ($matches[1] as $match) { - $dataProviderMethodNameNamespace = \explode('\\', $match); - $leaf = \explode('::', \array_pop($dataProviderMethodNameNamespace)); - $dataProviderMethodName = \array_pop($leaf); - - if (empty($dataProviderMethodNameNamespace)) { - $dataProviderMethodNameNamespace = ''; - } else { - $dataProviderMethodNameNamespace = \implode('\\', $dataProviderMethodNameNamespace) . '\\'; - } - - if (empty($leaf)) { - $dataProviderClassName = $className; - } else { - $dataProviderClassName = $dataProviderMethodNameNamespace . \array_pop($leaf); - } - - $dataProviderClass = new ReflectionClass($dataProviderClassName); - $dataProviderMethod = $dataProviderClass->getMethod( - $dataProviderMethodName - ); - - if ($dataProviderMethod->isStatic()) { - $object = null; - } else { - $object = $dataProviderClass->newInstance(); - } - - if ($dataProviderMethod->getNumberOfParameters() === 0) { - $data = $dataProviderMethod->invoke($object); - } else { - $data = $dataProviderMethod->invoke($object, $methodName); - } - - if ($data instanceof Traversable) { - $origData = $data; - $data = []; - - foreach ($origData as $key => $value) { - if (\is_int($key)) { - $data[] = $value; - } else { - $data[$key] = $value; - } - } - } - - if (\is_array($data)) { - $result = \array_merge($result, $data); - } - } - - return $result; - } - - return null; - } - - private static function cleanUpMultiLineAnnotation(string $docComment): string - { - //removing initial ' * ' for docComment - $docComment = \str_replace("\r\n", "\n", $docComment); - $docComment = \preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment); - $docComment = \substr($docComment, 0, -1); - - return \rtrim($docComment, "\n"); - } - - private static function emptyHookMethodsArray(): array - { - return [ - 'beforeClass' => ['setUpBeforeClass'], - 'before' => ['setUp'], - 'after' => ['tearDown'], - 'afterClass' => ['tearDownAfterClass'], - ]; - } - - private static function getBooleanAnnotationSetting(string $className, ?string $methodName, string $settingName): ?bool - { - $annotations = self::parseTestMethodAnnotations( - $className, - $methodName - ); - - if (isset($annotations['method'][$settingName])) { - if ($annotations['method'][$settingName][0] === 'enabled') { - return true; - } - - if ($annotations['method'][$settingName][0] === 'disabled') { - return false; - } - } - - if (isset($annotations['class'][$settingName])) { - if ($annotations['class'][$settingName][0] === 'enabled') { - return true; - } - - if ($annotations['class'][$settingName][0] === 'disabled') { - return false; - } - } - - return null; - } - - /** - * @throws InvalidCoversTargetException - */ - private static function resolveElementToReflectionObjects(string $element): array - { - $codeToCoverList = []; - - if (\strpos($element, '\\') !== false && \function_exists($element)) { - $codeToCoverList[] = new ReflectionFunction($element); - } elseif (\strpos($element, '::') !== false) { - [$className, $methodName] = \explode('::', $element); - - if (isset($methodName[0]) && $methodName[0] === '<') { - $classes = [$className]; - - foreach ($classes as $className) { - if (!\class_exists($className) && - !\interface_exists($className) && - !\trait_exists($className)) { - throw new InvalidCoversTargetException( - \sprintf( - 'Trying to @cover or @use not existing class or ' . - 'interface "%s".', - $className - ) - ); - } - - $class = new ReflectionClass($className); - $methods = $class->getMethods(); - $inverse = isset($methodName[1]) && $methodName[1] === '!'; - $visibility = 'isPublic'; - - if (\strpos($methodName, 'protected')) { - $visibility = 'isProtected'; - } elseif (\strpos($methodName, 'private')) { - $visibility = 'isPrivate'; - } - - foreach ($methods as $method) { - if ($inverse && !$method->$visibility()) { - $codeToCoverList[] = $method; - } elseif (!$inverse && $method->$visibility()) { - $codeToCoverList[] = $method; - } - } - } - } else { - $classes = [$className]; - - foreach ($classes as $className) { - if ($className === '' && \function_exists($methodName)) { - $codeToCoverList[] = new ReflectionFunction( - $methodName - ); - } else { - if (!((\class_exists($className) || \interface_exists($className) || \trait_exists($className)) && - \method_exists($className, $methodName))) { - throw new InvalidCoversTargetException( - \sprintf( - 'Trying to @cover or @use not existing method "%s::%s".', - $className, - $methodName - ) - ); - } - - $codeToCoverList[] = new ReflectionMethod( - $className, - $methodName - ); - } - } - } - } else { - $extended = false; - - if (\strpos($element, '') !== false) { - $element = \str_replace('', '', $element); - $extended = true; - } - - $classes = [$element]; - - if ($extended) { - $classes = \array_merge( - $classes, - \class_implements($element), - \class_parents($element) - ); - } - - foreach ($classes as $className) { - if (!\class_exists($className) && - !\interface_exists($className) && - !\trait_exists($className)) { - throw new InvalidCoversTargetException( - \sprintf( - 'Trying to @cover or @use not existing class or ' . - 'interface "%s".', - $className - ) - ); - } - - $codeToCoverList[] = new ReflectionClass($className); - } - } - - return $codeToCoverList; - } - - private static function resolveReflectionObjectsToLines(array $reflectors): array - { - $result = []; - - foreach ($reflectors as $reflector) { - if ($reflector instanceof ReflectionClass) { - foreach ($reflector->getTraits() as $trait) { - $reflectors[] = $trait; - } - } - } - - foreach ($reflectors as $reflector) { - $filename = $reflector->getFileName(); - - if (!isset($result[$filename])) { - $result[$filename] = []; - } - - $result[$filename] = \array_merge( - $result[$filename], - \range($reflector->getStartLine(), $reflector->getEndLine()) - ); - } - - foreach ($result as $filename => $lineNumbers) { - $result[$filename] = \array_keys(\array_flip($lineNumbers)); - } - - return $result; - } - - /** - * Trims any extensions from version string that follows after - * the .[.] format - */ - private static function sanitizeVersionNumber(string $version) - { - return \preg_replace( - '/^(\d+\.\d+(?:.\d+)?).*$/', - '$1', - $version - ); - } - - private static function shouldCoversAnnotationBeUsed(array $annotations): bool - { - if (isset($annotations['method']['coversNothing'])) { - return false; - } - - if (isset($annotations['method']['covers'])) { - return true; - } - - if (isset($annotations['class']['coversNothing'])) { - return false; - } - - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Error\Deprecated; -use PHPUnit\Framework\Error\Error; -use PHPUnit\Framework\Error\Notice; -use PHPUnit\Framework\Error\Warning; - -/** - * Error handler that converts PHP errors and warnings to exceptions. - */ -final class ErrorHandler -{ - private static $errorStack = []; - - /** - * Returns the error stack. - */ - public static function getErrorStack(): array - { - return self::$errorStack; - } - - public static function handleError(int $errorNumber, string $errorString, string $errorFile, int $errorLine): bool - { - if (!($errorNumber & \error_reporting())) { - return false; - } - - self::$errorStack[] = [$errorNumber, $errorString, $errorFile, $errorLine]; - - $trace = \debug_backtrace(); - \array_shift($trace); - - foreach ($trace as $frame) { - if ($frame['function'] === '__toString') { - return false; - } - } - - if ($errorNumber === \E_NOTICE || $errorNumber === \E_USER_NOTICE || $errorNumber === \E_STRICT) { - if (Notice::$enabled !== true) { - return false; - } - - $exception = Notice::class; - } elseif ($errorNumber === \E_WARNING || $errorNumber === \E_USER_WARNING) { - if (Warning::$enabled !== true) { - return false; - } - - $exception = Warning::class; - } elseif ($errorNumber === \E_DEPRECATED || $errorNumber === \E_USER_DEPRECATED) { - if (Deprecated::$enabled !== true) { - return false; - } - - $exception = Deprecated::class; - } else { - $exception = Error::class; - } - - throw new $exception($errorString, $errorNumber, $errorFile, $errorLine); - } - - /** - * Registers an error handler and returns a function that will restore - * the previous handler when invoked - * - * @param int $severity PHP predefined error constant - * - * @throws \Exception if event of specified severity is emitted - */ - public static function handleErrorOnce($severity = \E_WARNING): callable - { - $terminator = function () { - static $expired = false; - - if (!$expired) { - $expired = true; - - return \restore_error_handler(); - } - }; - - \set_error_handler( - function ($errorNumber, $errorString) use ($severity) { - if ($errorNumber === $severity) { - return; - } - - return false; - } - ); - - return $terminator; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\SyntheticError; - -final class Filter -{ - public static function getFilteredStacktrace(\Throwable $t): string - { - $prefix = false; - $script = \realpath($GLOBALS['_SERVER']['SCRIPT_NAME']); - - if (\defined('__PHPUNIT_PHAR_ROOT__')) { - $prefix = __PHPUNIT_PHAR_ROOT__; - } - - $filteredStacktrace = ''; - - if ($t instanceof SyntheticError) { - $eTrace = $t->getSyntheticTrace(); - $eFile = $t->getSyntheticFile(); - $eLine = $t->getSyntheticLine(); - } elseif ($t instanceof Exception) { - $eTrace = $t->getSerializableTrace(); - $eFile = $t->getFile(); - $eLine = $t->getLine(); - } else { - if ($t->getPrevious()) { - $t = $t->getPrevious(); - } - - $eTrace = $t->getTrace(); - $eFile = $t->getFile(); - $eLine = $t->getLine(); - } - - if (!self::frameExists($eTrace, $eFile, $eLine)) { - \array_unshift( - $eTrace, - ['file' => $eFile, 'line' => $eLine] - ); - } - - $blacklist = new Blacklist; - - foreach ($eTrace as $frame) { - if (isset($frame['file']) && \is_file($frame['file']) && - (empty($GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST']) || !\in_array($frame['file'], $GLOBALS['__PHPUNIT_ISOLATION_BLACKLIST'])) && - !$blacklist->isBlacklisted($frame['file']) && - ($prefix === false || \strpos($frame['file'], $prefix) !== 0) && - $frame['file'] !== $script) { - $filteredStacktrace .= \sprintf( - "%s:%s\n", - $frame['file'], - $frame['line'] ?? '?' - ); - } - } - - return $filteredStacktrace; - } - - private static function frameExists(array $trace, string $file, int $line): bool - { - foreach ($trace as $frame) { - if (isset($frame['file']) && $frame['file'] === $file && - isset($frame['line']) && $frame['line'] === $line) { - return true; - } - } - - return false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -/** - * Prints TestDox documentation in HTML format. - */ -final class HtmlResultPrinter extends ResultPrinter -{ - /** - * @var string - */ - private const PAGE_HEADER = << - - - - Test Documentation - - - -EOT; - - /** - * @var string - */ - private const CLASS_HEADER = <<%s -
      - -EOT; - - /** - * @var string - */ - private const CLASS_FOOTER = << -EOT; - - /** - * @var string - */ - private const PAGE_FOOTER = << - -EOT; - - /** - * Handler for 'start run' event. - */ - protected function startRun(): void - { - $this->write(self::PAGE_HEADER); - } - - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name): void - { - $this->write( - \sprintf( - self::CLASS_HEADER, - $name, - $this->currentTestClassPrettified - ) - ); - } - - /** - * Handler for 'on test' event. - */ - protected function onTest($name, bool $success = true): void - { - $this->write( - \sprintf( - "
    • %s %s
    • \n", - $success ? '#555753' : '#ef2929', - $success ? '✓' : '❌', - $name - ) - ); - } - - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name): void - { - $this->write(self::CLASS_FOOTER); - } - - /** - * Handler for 'end run' event. - */ - protected function endRun(): void - { - $this->write(self::PAGE_FOOTER); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -use PHPUnit\Framework\TestCase; -use SebastianBergmann\Exporter\Exporter; - -/** - * Prettifies class and method names for use in TestDox documentation. - */ -final class NamePrettifier -{ - /** - * @var array - */ - private $strings = []; - - /** - * Prettifies the name of a test class. - */ - public function prettifyTestClass(string $className): string - { - try { - $annotations = \PHPUnit\Util\Test::parseTestMethodAnnotations($className); - - if (isset($annotations['class']['testdox'][0])) { - return $annotations['class']['testdox'][0]; - } - } catch (\ReflectionException $e) { - } - - $result = $className; - - if (\substr($className, -1 * \strlen('Test')) === 'Test') { - $result = \substr($result, 0, \strripos($result, 'Test')); - } - - if (\strpos($className, 'Tests') === 0) { - $result = \substr($result, \strlen('Tests')); - } elseif (\strpos($className, 'Test') === 0) { - $result = \substr($result, \strlen('Test')); - } - - if ($result[0] === '\\') { - $result = \substr($result, 1); - } - - return $result; - } - - /** - * @throws \ReflectionException - */ - public function prettifyTestCase(TestCase $test): string - { - $annotations = $test->getAnnotations(); - $annotationWithPlaceholders = false; - - $callback = static function (string $variable): string { - return \sprintf('/%s(?=\b)/', \preg_quote($variable, '/')); - }; - - if (isset($annotations['method']['testdox'][0])) { - $result = $annotations['method']['testdox'][0]; - - if (\strpos($result, '$') !== false) { - $annotation = $annotations['method']['testdox'][0]; - $providedData = $this->mapTestMethodParameterNamesToProvidedDataValues($test); - $variables = \array_map($callback, \array_keys($providedData)); - - $result = \trim(\preg_replace($variables, $providedData, $annotation)); - - $annotationWithPlaceholders = true; - } - } else { - $result = $this->prettifyTestMethod($test->getName(false)); - } - - if ($test->usesDataProvider() && !$annotationWithPlaceholders) { - $result .= $test->getDataSetAsString(false); - } - - return $result; - } - - /** - * Prettifies the name of a test method. - */ - public function prettifyTestMethod(string $name): string - { - $buffer = ''; - - if (!\is_string($name) || $name === '') { - return $buffer; - } - - $string = \preg_replace('#\d+$#', '', $name, -1, $count); - - if (\in_array($string, $this->strings)) { - $name = $string; - } elseif ($count === 0) { - $this->strings[] = $string; - } - - if (\strpos($name, 'test_') === 0) { - $name = \substr($name, 5); - } elseif (\strpos($name, 'test') === 0) { - $name = \substr($name, 4); - } - - if ($name === '') { - return $buffer; - } - - $name[0] = \strtoupper($name[0]); - - if (\strpos($name, '_') !== false) { - return \trim(\str_replace('_', ' ', $name)); - } - - $max = \strlen($name); - $wasNumeric = false; - - for ($i = 0; $i < $max; $i++) { - if ($i > 0 && \ord($name[$i]) >= 65 && \ord($name[$i]) <= 90) { - $buffer .= ' ' . \strtolower($name[$i]); - } else { - $isNumeric = \is_numeric($name[$i]); - - if (!$wasNumeric && $isNumeric) { - $buffer .= ' '; - $wasNumeric = true; - } - - if ($wasNumeric && !$isNumeric) { - $wasNumeric = false; - } - - $buffer .= $name[$i]; - } - } - - return $buffer; - } - - /** - * @throws \ReflectionException - */ - private function mapTestMethodParameterNamesToProvidedDataValues(TestCase $test): array - { - $reflector = new \ReflectionMethod(\get_class($test), $test->getName(false)); - $providedData = []; - $providedDataValues = \array_values($test->getProvidedData()); - $i = 0; - - foreach ($reflector->getParameters() as $parameter) { - if (!\array_key_exists($i, $providedDataValues) && $parameter->isDefaultValueAvailable()) { - $providedDataValues[$i] = $parameter->getDefaultValue(); - } - - $value = $providedDataValues[$i++] ?? null; - - if (\is_object($value)) { - $reflector = new \ReflectionObject($value); - - if ($reflector->hasMethod('__toString')) { - $value = (string) $value; - } - } - - if (!\is_scalar($value)) { - $value = \gettype($value); - } - - if (\is_bool($value) || \is_int($value) || \is_float($value)) { - $exporter = new Exporter; - - $value = $exporter->export($value); - } - - $providedData['$' . $parameter->getName()] = $value; - } - - return $providedData; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -/** - * Prints TestDox documentation in text format to files. - * For the CLI testdox printer please refer to \PHPUnit\TextUI\TextDoxPrinter. - */ -class TextResultPrinter extends ResultPrinter -{ - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name): void - { - $this->write($this->currentTestClassPrettified . "\n"); - } - - /** - * Handler for 'on test' event. - */ - protected function onTest($name, bool $success = true): void - { - if ($success) { - $this->write(' [x] '); - } else { - $this->write(' [ ] '); - } - - $this->write($name . "\n"); - } - - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name): void - { - $this->write("\n"); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestResult; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\TextUI\ResultPrinter; -use SebastianBergmann\Timer\Timer; - -/** - * This printer is for CLI output only. For the classes that output to file, html and xml, - * please refer to the PHPUnit\Util\TestDox namespace - */ -class CliTestDoxPrinter extends ResultPrinter -{ - /** - * @var int[] - */ - private $nonSuccessfulTestResults = []; - - /** - * @var NamePrettifier - */ - private $prettifier; - - /** - * @var int The number of test results received from the TestRunner - */ - private $testIndex = 0; - - /** - * @var int The number of test results already sent to the output - */ - private $testFlushIndex = 0; - - /** - * @var array Buffer for write() - */ - private $outputBuffer = []; - - /** - * @var bool - */ - private $bufferExecutionOrder = false; - - /** - * @var array array - */ - private $originalExecutionOrder = []; - - /** - * @var string Classname of the current test - */ - private $className = ''; - - /** - * @var string Classname of the previous test; empty for first test - */ - private $lastClassName = ''; - - /** - * @var string Prettified test name of current test - */ - private $testMethod; - - /** - * @var string Test result message of current test - */ - private $testResultMessage; - - /** - * @var bool Test result message of current test contains a verbose dump - */ - private $lastFlushedTestWasVerbose = false; - - public function __construct( - $out = null, - bool $verbose = false, - $colors = self::COLOR_DEFAULT, - bool $debug = false, - $numberOfColumns = 80, - bool $reverse = false - ) { - parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse); - - $this->prettifier = new NamePrettifier; - } - - public function setOriginalExecutionOrder(array $order): void - { - $this->originalExecutionOrder = $order; - $this->bufferExecutionOrder = !empty($order); - } - - public function startTest(Test $test): void - { - if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) { - return; - } - - $this->lastTestFailed = false; - $this->lastClassName = $this->className; - $this->testResultMessage = ''; - - if ($test instanceof TestCase) { - $className = $this->prettifier->prettifyTestClass(\get_class($test)); - $testMethod = $this->prettifier->prettifyTestCase($test); - } elseif ($test instanceof PhptTestCase) { - $className = \get_class($test); - $testMethod = $test->getName(); - } - - $this->className = $className; - $this->testMethod = $testMethod; - - parent::startTest($test); - } - - public function endTest(Test $test, float $time): void - { - if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) { - return; - } - - if ($test instanceof TestCase || $test instanceof PhptTestCase) { - $this->testIndex++; - } - - if ($this->lastTestFailed) { - $resultMessage = $this->testResultMessage; - $this->nonSuccessfulTestResults[] = $this->testIndex; - } else { - $resultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-green', '✔'), - '', - $time, - $this->verbose - ); - } - - if ($this->bufferExecutionOrder) { - $this->bufferTestResult($test, $resultMessage); - $this->flushOutputBuffer(); - } else { - $this->writeTestResult($resultMessage); - - if ($this->lastTestFailed) { - $this->bufferTestResult($test, $resultMessage); - } - } - - parent::endTest($test, $time); - } - - public function addError(Test $test, \Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->testResultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-yellow', '✘'), - (string) $t, - $time, - true - ); - } - - public function addWarning(Test $test, Warning $e, float $time): void - { - $this->lastTestFailed = true; - $this->testResultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-yellow', '✘'), - (string) $e, - $time, - true - ); - } - - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - $this->lastTestFailed = true; - $this->testResultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-red', '✘'), - (string) $e, - $time, - true - ); - } - - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->testResultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-yellow', '∅'), - (string) $t, - $time, - false - ); - } - - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->testResultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-yellow', '☢'), - (string) $t, - $time, - false - ); - } - - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->testResultMessage = $this->formatTestResultMessage( - $this->formatWithColor('fg-yellow', '→'), - (string) $t, - $time, - false - ); - } - - public function bufferTestResult(Test $test, string $msg): void - { - $this->outputBuffer[$this->testIndex] = [ - 'className' => $this->className, - 'testName' => TestSuiteSorter::getTestSorterUID($test), - 'testMethod' => $this->testMethod, - 'message' => $msg, - 'failed' => $this->lastTestFailed, - 'verbose' => $this->lastFlushedTestWasVerbose, - ]; - } - - public function writeTestResult(string $msg): void - { - $msg = $this->formatTestSuiteHeader($this->lastClassName, $this->className, $msg); - $this->write($msg); - } - - public function writeProgress(string $progress): void - { - } - - public function flush(): void - { - } - - public function printResult(TestResult $result): void - { - $this->printHeader(); - - $this->printNonSuccessfulTestsSummary($result->count()); - - $this->printFooter($result); - } - - protected function printHeader(): void - { - $this->write("\n" . Timer::resourceUsage() . "\n\n"); - } - - private function flushOutputBuffer(): void - { - if ($this->testFlushIndex === $this->testIndex) { - return; - } - - if ($this->testFlushIndex > 0) { - $prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]); - } else { - $prevResult = $this->getEmptyTestResult(); - } - - do { - $flushed = false; - $result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]); - - if (!empty($result)) { - $this->writeBufferTestResult($prevResult, $result); - $this->testFlushIndex++; - $prevResult = $result; - $flushed = true; - } - } while ($flushed && $this->testFlushIndex < $this->testIndex); - } - - private function writeBufferTestResult(array $prevResult, array $result): void - { - // Write spacer line for new suite headers and after verbose messages - if ($prevResult['testName'] !== '' && - ($prevResult['verbose'] === true || $prevResult['className'] !== $result['className'])) { - $this->write("\n"); - } - - // Write suite header - if ($prevResult['className'] !== $result['className']) { - $this->write($result['className'] . "\n"); - } - - // Write the test result itself - $this->write($result['message']); - } - - private function getTestResultByName(string $testName): array - { - foreach ($this->outputBuffer as $result) { - if ($result['testName'] === $testName) { - return $result; - } - } - - return []; - } - - private function formatTestSuiteHeader(?string $lastClassName, string $className, string $msg): string - { - if ($lastClassName === null || $className !== $lastClassName) { - return \sprintf( - "%s%s\n%s", - ($this->lastClassName !== '') ? "\n" : '', - $className, - $msg - ); - } - - return $msg; - } - - private function formatTestResultMessage( - string $symbol, - string $resultMessage, - float $time, - bool $alwaysVerbose = false - ): string { - $additionalInformation = $this->getFormattedAdditionalInformation($resultMessage, $alwaysVerbose); - $msg = \sprintf( - " %s %s%s\n%s", - $symbol, - $this->testMethod, - $this->verbose ? ' ' . $this->getFormattedRuntime($time) : '', - $additionalInformation - ); - - $this->lastFlushedTestWasVerbose = !empty($additionalInformation); - - return $msg; - } - - private function getFormattedRuntime(float $time): string - { - if ($time > 5) { - return $this->formatWithColor('fg-red', \sprintf('[%.2f ms]', $time * 1000)); - } - - if ($time > 1) { - return $this->formatWithColor('fg-yellow', \sprintf('[%.2f ms]', $time * 1000)); - } - - return \sprintf('[%.2f ms]', $time * 1000); - } - - private function getFormattedAdditionalInformation(string $resultMessage, bool $verbose): string - { - if ($resultMessage === '') { - return ''; - } - - if (!($this->verbose || $verbose)) { - return ''; - } - - return \sprintf( - " │\n%s\n", - \implode( - "\n", - \array_map( - function (string $text) { - return \sprintf(' │ %s', $text); - }, - \explode("\n", $resultMessage) - ) - ) - ); - } - - private function printNonSuccessfulTestsSummary(int $numberOfExecutedTests): void - { - if (empty($this->nonSuccessfulTestResults)) { - return; - } - - if ((\count($this->nonSuccessfulTestResults) / $numberOfExecutedTests) >= 0.7) { - return; - } - - $this->write("Summary of non-successful tests:\n\n"); - - $prevResult = $this->getEmptyTestResult(); - - foreach ($this->nonSuccessfulTestResults as $testIndex) { - $result = $this->outputBuffer[$testIndex]; - $this->writeBufferTestResult($prevResult, $result); - $prevResult = $result; - } - } - - private function getEmptyTestResult(): array - { - return [ - 'className' => '', - 'testName' => '', - 'message' => '', - 'failed' => '', - 'verbose' => '', - ]; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -final class TestResult -{ - /** - * @var callable - */ - private $colorize; - - /** - * @var string - */ - private $testClass; - - /** - * @var string - */ - private $testMethod; - - /** - * @var bool - */ - private $testSuccesful; - - /** - * @var string - */ - private $symbol; - - /** - * @var string - */ - private $additionalInformation; - - /** - * @var bool - */ - private $additionalInformationVerbose; - - /** - * @var float - */ - private $runtime; - - public function __construct(callable $colorize, string $testClass, string $testMethod) - { - $this->colorize = $colorize; - $this->testClass = $testClass; - $this->testMethod = $testMethod; - $this->testSuccesful = true; - $this->symbol = ($this->colorize)('fg-green', '✔'); - $this->additionalInformation = ''; - } - - public function isTestSuccessful(): bool - { - return $this->testSuccesful; - } - - public function fail(string $symbol, string $additionalInformation, bool $additionalInformationVerbose = false): void - { - $this->testSuccesful = false; - $this->symbol = $symbol; - $this->additionalInformation = $additionalInformation; - $this->additionalInformationVerbose = $additionalInformationVerbose; - } - - public function setRuntime(float $runtime): void - { - $this->runtime = $runtime; - } - - public function toString(?self $previousTestResult, $verbose = false): string - { - return \sprintf( - "%s%s %s %s%s\n%s", - $previousTestResult && $previousTestResult->additionalInformationPrintable($verbose) ? "\n" : '', - $this->getClassNameHeader($previousTestResult ? $previousTestResult->testClass : null), - $this->symbol, - $this->testMethod, - $verbose ? ' ' . $this->getFormattedRuntime() : '', - $this->getFormattedAdditionalInformation($verbose) - ); - } - - private function getClassNameHeader(?string $previousTestClass): string - { - $className = ''; - - if ($this->testClass !== $previousTestClass) { - if (null !== $previousTestClass) { - $className = "\n"; - } - - $className .= \sprintf("%s\n", $this->testClass); - } - - return $className; - } - - private function getFormattedRuntime(): string - { - if ($this->runtime > 5) { - return ($this->colorize)('fg-red', \sprintf('[%.2f ms]', $this->runtime * 1000)); - } - - if ($this->runtime > 1) { - return ($this->colorize)('fg-yellow', \sprintf('[%.2f ms]', $this->runtime * 1000)); - } - - return \sprintf('[%.2f ms]', $this->runtime * 1000); - } - - private function getFormattedAdditionalInformation($verbose): string - { - if (!$this->additionalInformationPrintable($verbose)) { - return ''; - } - - return \sprintf( - " │\n%s\n", - \implode( - "\n", - \array_map( - function (string $text) { - return \sprintf(' │ %s', $text); - }, - \explode("\n", $this->additionalInformation) - ) - ) - ); - } - - private function additionalInformationPrintable(bool $verbose): bool - { - if ($this->additionalInformation === '') { - return false; - } - - if ($this->additionalInformationVerbose && !$verbose) { - return false; - } - - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -use DOMDocument; -use DOMElement; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Util\Printer; -use ReflectionClass; - -class XmlResultPrinter extends Printer implements TestListener -{ - /** - * @var DOMDocument - */ - private $document; - - /** - * @var DOMElement - */ - private $root; - - /** - * @var NamePrettifier - */ - private $prettifier; - - /** - * @var null|\Throwable - */ - private $exception; - - /** - * @param resource|string $out - * - * @throws Exception - */ - public function __construct($out = null) - { - $this->document = new DOMDocument('1.0', 'UTF-8'); - $this->document->formatOutput = true; - - $this->root = $this->document->createElement('tests'); - $this->document->appendChild($this->root); - - $this->prettifier = new NamePrettifier; - - parent::__construct($out); - } - - /** - * Flush buffer and close output. - */ - public function flush(): void - { - $this->write($this->document->saveXML()); - - parent::flush(); - } - - /** - * An error occurred. - */ - public function addError(Test $test, \Throwable $t, float $time): void - { - $this->exception = $t; - } - - /** - * A warning occurred. - */ - public function addWarning(Test $test, Warning $e, float $time): void - { - } - - /** - * A failure occurred. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - $this->exception = $e; - } - - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - } - - /** - * Risky test. - */ - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - } - - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - } - - /** - * A test suite started. - */ - public function startTestSuite(TestSuite $suite): void - { - } - - /** - * A test suite ended. - */ - public function endTestSuite(TestSuite $suite): void - { - } - - /** - * A test started. - */ - public function startTest(Test $test): void - { - $this->exception = null; - } - - /** - * A test ended. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function endTest(Test $test, float $time): void - { - if (!$test instanceof TestCase) { - return; - } - - /* @var TestCase $test */ - - $groups = \array_filter( - $test->getGroups(), - function ($group) { - return !($group === 'small' || $group === 'medium' || $group === 'large'); - } - ); - - $node = $this->document->createElement('test'); - - $node->setAttribute('className', \get_class($test)); - $node->setAttribute('methodName', $test->getName()); - $node->setAttribute('prettifiedClassName', $this->prettifier->prettifyTestClass(\get_class($test))); - $node->setAttribute('prettifiedMethodName', $this->prettifier->prettifyTestCase($test)); - $node->setAttribute('status', $test->getStatus()); - $node->setAttribute('time', $time); - $node->setAttribute('size', $test->getSize()); - $node->setAttribute('groups', \implode(',', $groups)); - - $inlineAnnotations = \PHPUnit\Util\Test::getInlineAnnotations(\get_class($test), $test->getName()); - - if (isset($inlineAnnotations['given'], $inlineAnnotations['when'], $inlineAnnotations['then'])) { - $node->setAttribute('given', $inlineAnnotations['given']['value']); - $node->setAttribute('givenStartLine', $inlineAnnotations['given']['line']); - $node->setAttribute('when', $inlineAnnotations['when']['value']); - $node->setAttribute('whenStartLine', $inlineAnnotations['when']['line']); - $node->setAttribute('then', $inlineAnnotations['then']['value']); - $node->setAttribute('thenStartLine', $inlineAnnotations['then']['line']); - } - - if ($this->exception !== null) { - if ($this->exception instanceof Exception) { - $steps = $this->exception->getSerializableTrace(); - } else { - $steps = $this->exception->getTrace(); - } - - $class = new ReflectionClass($test); - $file = $class->getFileName(); - - foreach ($steps as $step) { - if (isset($step['file']) && $step['file'] === $file) { - $node->setAttribute('exceptionLine', $step['line']); - - break; - } - } - - $node->setAttribute('exceptionMessage', $this->exception->getMessage()); - } - - $this->root->appendChild($node); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\TestDox; - -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Framework\WarningTestCase; -use PHPUnit\Runner\BaseTestRunner; -use PHPUnit\Util\Printer; - -/** - * Base class for printers of TestDox documentation. - */ -abstract class ResultPrinter extends Printer implements TestListener -{ - /** - * @var NamePrettifier - */ - protected $prettifier; - - /** - * @var string - */ - protected $testClass = ''; - - /** - * @var int - */ - protected $testStatus; - - /** - * @var array - */ - protected $tests = []; - - /** - * @var int - */ - protected $successful = 0; - - /** - * @var int - */ - protected $warned = 0; - - /** - * @var int - */ - protected $failed = 0; - - /** - * @var int - */ - protected $risky = 0; - - /** - * @var int - */ - protected $skipped = 0; - - /** - * @var int - */ - protected $incomplete = 0; - - /** - * @var null|string - */ - protected $currentTestClassPrettified; - - /** - * @var null|string - */ - protected $currentTestMethodPrettified; - - /** - * @var array - */ - private $groups; - - /** - * @var array - */ - private $excludeGroups; - - /** - * @param resource $out - * - * @throws \PHPUnit\Framework\Exception - */ - public function __construct($out = null, array $groups = [], array $excludeGroups = []) - { - parent::__construct($out); - - $this->groups = $groups; - $this->excludeGroups = $excludeGroups; - - $this->prettifier = new NamePrettifier; - $this->startRun(); - } - - /** - * Flush buffer and close output. - */ - public function flush(): void - { - $this->doEndClass(); - $this->endRun(); - - parent::flush(); - } - - /** - * An error occurred. - */ - public function addError(Test $test, \Throwable $t, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->testStatus = BaseTestRunner::STATUS_ERROR; - $this->failed++; - } - - /** - * A warning occurred. - */ - public function addWarning(Test $test, Warning $e, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->testStatus = BaseTestRunner::STATUS_WARNING; - $this->warned++; - } - - /** - * A failure occurred. - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->testStatus = BaseTestRunner::STATUS_FAILURE; - $this->failed++; - } - - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->testStatus = BaseTestRunner::STATUS_INCOMPLETE; - $this->incomplete++; - } - - /** - * Risky test. - */ - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->testStatus = BaseTestRunner::STATUS_RISKY; - $this->risky++; - } - - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->testStatus = BaseTestRunner::STATUS_SKIPPED; - $this->skipped++; - } - - /** - * A testsuite started. - */ - public function startTestSuite(TestSuite $suite): void - { - } - - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite): void - { - } - - /** - * A test started. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function startTest(Test $test): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $class = \get_class($test); - - if ($this->testClass !== $class) { - if ($this->testClass !== '') { - $this->doEndClass(); - } - - $this->currentTestClassPrettified = $this->prettifier->prettifyTestClass($class); - $this->testClass = $class; - $this->tests = []; - - $this->startClass($class); - } - - if ($test instanceof TestCase) { - $this->currentTestMethodPrettified = $this->prettifier->prettifyTestCase($test); - } - - $this->testStatus = BaseTestRunner::STATUS_PASSED; - } - - /** - * A test ended. - */ - public function endTest(Test $test, float $time): void - { - if (!$this->isOfInterest($test)) { - return; - } - - $this->tests[] = [$this->currentTestMethodPrettified, $this->testStatus]; - - $this->currentTestClassPrettified = null; - $this->currentTestMethodPrettified = null; - } - - protected function doEndClass(): void - { - foreach ($this->tests as $test) { - $this->onTest($test[0], $test[1] === BaseTestRunner::STATUS_PASSED); - } - - $this->endClass($this->testClass); - } - - /** - * Handler for 'start run' event. - */ - protected function startRun(): void - { - } - - /** - * Handler for 'start class' event. - */ - protected function startClass(string $name): void - { - } - - /** - * Handler for 'on test' event. - */ - protected function onTest($name, bool $success = true): void - { - } - - /** - * Handler for 'end class' event. - */ - protected function endClass(string $name): void - { - } - - /** - * Handler for 'end run' event. - */ - protected function endRun(): void - { - } - - private function isOfInterest(Test $test): bool - { - if (!$test instanceof TestCase) { - return false; - } - - if ($test instanceof WarningTestCase) { - return false; - } - - if (!empty($this->groups)) { - foreach ($test->getGroups() as $group) { - if (\in_array($group, $this->groups)) { - return true; - } - } - - return false; - } - - if (!empty($this->excludeGroups)) { - foreach ($test->getGroups() as $group) { - if (\in_array($group, $this->excludeGroups)) { - return false; - } - } - - return true; - } - - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Exception; - -/** - * Factory for PHPUnit\Framework\Exception objects that are used to describe - * invalid arguments passed to a function or method. - */ -final class InvalidArgumentHelper -{ - public static function factory(int $argument, string $type, $value = null): Exception - { - $stack = \debug_backtrace(); - - return new Exception( - \sprintf( - 'Argument #%d%sof %s::%s() must be a %s', - $argument, - $value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ', - $stack[1]['class'], - $stack[1]['function'], - $type - ) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; - -final class XmlTestListRenderer -{ - public function render(TestSuite $suite): string - { - $writer = new \XMLWriter; - - $writer->openMemory(); - $writer->setIndent(true); - $writer->startDocument(); - $writer->startElement('tests'); - - $currentTestCase = null; - - foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) { - if ($test instanceof TestCase) { - if (\get_class($test) !== $currentTestCase) { - if ($currentTestCase !== null) { - $writer->endElement(); - } - - $writer->startElement('testCaseClass'); - $writer->writeAttribute('name', \get_class($test)); - - $currentTestCase = \get_class($test); - } - - $writer->startElement('testCaseMethod'); - $writer->writeAttribute('name', $test->getName(false)); - $writer->writeAttribute('groups', \implode(',', $test->getGroups())); - - if (!empty($test->getDataSetAsString(false))) { - $writer->writeAttribute( - 'dataSet', - \str_replace( - ' with data set ', - '', - $test->getDataSetAsString(false) - ) - ); - } - - $writer->endElement(); - } elseif ($test instanceof PhptTestCase) { - if ($currentTestCase !== null) { - $writer->endElement(); - - $currentTestCase = null; - } - - $writer->startElement('phptFile'); - $writer->writeAttribute('path', $test->getName()); - $writer->endElement(); - } else { - continue; - } - } - - if ($currentTestCase !== null) { - $writer->endElement(); - } - - $writer->endElement(); - - return $writer->outputMemory(); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Exception; - -/** - * Command-line options parsing class. - */ -final class Getopt -{ - /** - * @throws Exception - */ - public static function getopt(array $args, string $short_options, array $long_options = null): array - { - if (empty($args)) { - return [[], []]; - } - - $opts = []; - $non_opts = []; - - if ($long_options) { - \sort($long_options); - } - - if (isset($args[0][0]) && $args[0][0] !== '-') { - \array_shift($args); - } - - \reset($args); - - $args = \array_map('trim', $args); - - /* @noinspection ComparisonOperandsOrderInspection */ - while (false !== $arg = \current($args)) { - $i = \key($args); - \next($args); - - if ($arg === '') { - continue; - } - - if ($arg === '--') { - $non_opts = \array_merge($non_opts, \array_slice($args, $i + 1)); - - break; - } - - if ($arg[0] !== '-' || (\strlen($arg) > 1 && $arg[1] === '-' && !$long_options)) { - $non_opts[] = $args[$i]; - - continue; - } - - if (\strlen($arg) > 1 && $arg[1] === '-') { - self::parseLongOption( - \substr($arg, 2), - $long_options, - $opts, - $args - ); - } else { - self::parseShortOption( - \substr($arg, 1), - $short_options, - $opts, - $args - ); - } - } - - return [$opts, $non_opts]; - } - - /** - * @throws Exception - */ - private static function parseShortOption(string $arg, string $short_options, array &$opts, array &$args): void - { - $argLen = \strlen($arg); - - for ($i = 0; $i < $argLen; $i++) { - $opt = $arg[$i]; - $opt_arg = null; - - if ($arg[$i] === ':' || ($spec = \strstr($short_options, $opt)) === false) { - throw new Exception( - "unrecognized option -- $opt" - ); - } - - if (\strlen($spec) > 1 && $spec[1] === ':') { - if ($i + 1 < $argLen) { - $opts[] = [$opt, \substr($arg, $i + 1)]; - - break; - } - - if (!(\strlen($spec) > 2 && $spec[2] === ':')) { - /* @noinspection ComparisonOperandsOrderInspection */ - if (false === $opt_arg = \current($args)) { - throw new Exception( - "option requires an argument -- $opt" - ); - } - - \next($args); - } - } - - $opts[] = [$opt, $opt_arg]; - } - } - - /** - * @throws Exception - */ - private static function parseLongOption(string $arg, array $long_options, array &$opts, array &$args): void - { - $count = \count($long_options); - $list = \explode('=', $arg); - $opt = $list[0]; - $opt_arg = null; - - if (\count($list) > 1) { - $opt_arg = $list[1]; - } - - $opt_len = \strlen($opt); - - for ($i = 0; $i < $count; $i++) { - $long_opt = $long_options[$i]; - $opt_start = \substr($long_opt, 0, $opt_len); - - if ($opt_start !== $opt) { - continue; - } - - $opt_rest = \substr($long_opt, $opt_len); - - if ($opt_rest !== '' && $i + 1 < $count && $opt[0] !== '=' && - \strpos($long_options[$i + 1], $opt) === 0) { - throw new Exception( - "option --$opt is ambiguous" - ); - } - - if (\substr($long_opt, -1) === '=') { - /* @noinspection StrlenInEmptyStringCheckContextInspection */ - if (\substr($long_opt, -2) !== '==' && !\strlen($opt_arg)) { - /* @noinspection ComparisonOperandsOrderInspection */ - if (false === $opt_arg = \current($args)) { - throw new Exception( - "option --$opt requires an argument" - ); - } - - \next($args); - } - } elseif ($opt_arg) { - throw new Exception( - "option --$opt doesn't allow an argument" - ); - } - - $full_option = '--' . \preg_replace('/={1,2}$/', '', $long_opt); - $opts[] = [$full_option, $opt_arg]; - - return; - } - - throw new Exception("unrecognized option --$opt"); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use Composer\Autoload\ClassLoader; -use DeepCopy\DeepCopy; -use Doctrine\Instantiator\Instantiator; -use PharIo\Manifest\Manifest; -use PharIo\Version\Version as PharIoVersion; -use PHP_Token; -use phpDocumentor\Reflection\DocBlock; -use phpDocumentor\Reflection\Project; -use phpDocumentor\Reflection\Type; -use PHPUnit\Framework\TestCase; -use Prophecy\Prophet; -use ReflectionClass; -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeUnitReverseLookup\Wizard; -use SebastianBergmann\Comparator\Comparator; -use SebastianBergmann\Diff\Diff; -use SebastianBergmann\Environment\Runtime; -use SebastianBergmann\Exporter\Exporter; -use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; -use SebastianBergmann\GlobalState\Snapshot; -use SebastianBergmann\Invoker\Invoker; -use SebastianBergmann\ObjectEnumerator\Enumerator; -use SebastianBergmann\RecursionContext\Context; -use SebastianBergmann\ResourceOperations\ResourceOperations; -use SebastianBergmann\Timer\Timer; -use SebastianBergmann\Version; -use Text_Template; -use TheSeer\Tokenizer\Tokenizer; -use Webmozart\Assert\Assert; - -/** - * Utility class for blacklisting PHPUnit's own source code files. - */ -final class Blacklist -{ - /** - * @var array - */ - public static $blacklistedClassNames = [ - // composer - ClassLoader::class => 1, - - // doctrine/instantiator - Instantiator::class => 1, - - // myclabs/deepcopy - DeepCopy::class => 1, - - // phar-io/manifest - Manifest::class => 1, - - // phar-io/version - PharIoVersion::class => 1, - - // phpdocumentor/reflection-common - Project::class => 1, - - // phpdocumentor/reflection-docblock - DocBlock::class => 1, - - // phpdocumentor/type-resolver - Type::class => 1, - - // phpspec/prophecy - Prophet::class => 1, - - // phpunit/phpunit - TestCase::class => 2, - - // phpunit/php-code-coverage - CodeCoverage::class => 1, - - // phpunit/php-file-iterator - FileIteratorFacade::class => 1, - - // phpunit/php-invoker - Invoker::class => 1, - - // phpunit/php-text-template - Text_Template::class => 1, - - // phpunit/php-timer - Timer::class => 1, - - // phpunit/php-token-stream - PHP_Token::class => 1, - - // sebastian/code-unit-reverse-lookup - Wizard::class => 1, - - // sebastian/comparator - Comparator::class => 1, - - // sebastian/diff - Diff::class => 1, - - // sebastian/environment - Runtime::class => 1, - - // sebastian/exporter - Exporter::class => 1, - - // sebastian/global-state - Snapshot::class => 1, - - // sebastian/object-enumerator - Enumerator::class => 1, - - // sebastian/recursion-context - Context::class => 1, - - // sebastian/resource-operations - ResourceOperations::class => 1, - - // sebastian/version - Version::class => 1, - - // theseer/tokenizer - Tokenizer::class => 1, - - // webmozart/assert - Assert::class => 1, - ]; - - /** - * @var string[] - */ - private static $directories; - - /** - * @return string[] - */ - public function getBlacklistedDirectories(): array - { - $this->initialize(); - - return self::$directories; - } - - public function isBlacklisted(string $file): bool - { - if (\defined('PHPUNIT_TESTSUITE')) { - return false; - } - - $this->initialize(); - - foreach (self::$directories as $directory) { - if (\strpos($file, $directory) === 0) { - return true; - } - } - - return false; - } - - private function initialize(): void - { - if (self::$directories === null) { - self::$directories = []; - - foreach (self::$blacklistedClassNames as $className => $parent) { - if (!\class_exists($className)) { - continue; - } - - $reflector = new ReflectionClass($className); - $directory = $reflector->getFileName(); - - for ($i = 0; $i < $parent; $i++) { - $directory = \dirname($directory); - } - - self::$directories[] = $directory; - } - - // Hide process isolation workaround on Windows. - if (\DIRECTORY_SEPARATOR === '\\') { - // tempnam() prefix is limited to first 3 chars. - // @see https://php.net/manual/en/function.tempnam.php - self::$directories[] = \sys_get_temp_dir() . '\\PHP'; - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -final class XdebugFilterScriptGenerator -{ - public function generate(array $filterData): string - { - $items = $this->getWhitelistItems($filterData); - - $files = \array_map( - function ($item) { - return \sprintf( - " '%s'", - $item - ); - }, - $items - ); - - $files = \implode(",\n", $files); - - return << - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -final class Type -{ - public static function isType(string $type): bool - { - switch ($type) { - case 'numeric': - case 'integer': - case 'int': - case 'iterable': - case 'float': - case 'string': - case 'boolean': - case 'bool': - case 'null': - case 'array': - case 'object': - case 'resource': - case 'scalar': - return true; - - default: - return false; - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -class NullTestResultCache implements TestResultCacheInterface -{ - public function getState($testName): int - { - return BaseTestRunner::STATUS_UNKNOWN; - } - - public function getTime($testName): float - { - return 0; - } - - public function load(): void - { - } - - public function persist(): void - { - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Exception; - -/** - * Utility class that can print to STDOUT or write to a file. - */ -class Printer -{ - /** - * If true, flush output after every write. - * - * @var bool - */ - protected $autoFlush = false; - - /** - * @var resource - */ - protected $out; - - /** - * @var string - */ - protected $outTarget; - - /** - * Constructor. - * - * @param null|mixed $out - * - * @throws Exception - */ - public function __construct($out = null) - { - if ($out !== null) { - if (\is_string($out)) { - if (\strpos($out, 'socket://') === 0) { - $out = \explode(':', \str_replace('socket://', '', $out)); - - if (\count($out) !== 2) { - throw new Exception; - } - - $this->out = \fsockopen($out[0], $out[1]); - } else { - if (\strpos($out, 'php://') === false && !Filesystem::createDirectory(\dirname($out))) { - throw new Exception(\sprintf('Directory "%s" was not created', \dirname($out))); - } - - $this->out = \fopen($out, 'wt'); - } - - $this->outTarget = $out; - } else { - $this->out = $out; - } - } - } - - /** - * Flush buffer and close output if it's not to a PHP stream - */ - public function flush(): void - { - if ($this->out && \strncmp($this->outTarget, 'php://', 6) !== 0) { - \fclose($this->out); - } - } - - /** - * Performs a safe, incremental flush. - * - * Do not confuse this function with the flush() function of this class, - * since the flush() function may close the file being written to, rendering - * the current object no longer usable. - */ - public function incrementalFlush(): void - { - if ($this->out) { - \fflush($this->out); - } else { - \flush(); - } - } - - public function write(string $buffer): void - { - if ($this->out) { - \fwrite($this->out, $buffer); - - if ($this->autoFlush) { - $this->incrementalFlush(); - } - } else { - if (\PHP_SAPI !== 'cli' && \PHP_SAPI !== 'phpdbg') { - $buffer = \htmlspecialchars($buffer, \ENT_SUBSTITUTE); - } - - print $buffer; - - if ($this->autoFlush) { - $this->incrementalFlush(); - } - } - } - - /** - * Check auto-flush mode. - */ - public function getAutoFlush(): bool - { - return $this->autoFlush; - } - - /** - * Set auto-flushing mode. - * - * If set, *incremental* flushes will be done after each write. This should - * not be confused with the different effects of this class' flush() method. - */ - public function setAutoFlush(bool $autoFlush): void - { - $this->autoFlush = $autoFlush; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Log; - -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\ExceptionWrapper; -use PHPUnit\Framework\ExpectationFailedException; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; -use PHPUnit\Framework\TestResult; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\TextUI\ResultPrinter; -use PHPUnit\Util\Filter; -use ReflectionClass; -use SebastianBergmann\Comparator\ComparisonFailure; - -/** - * A TestListener that generates a logfile of the test execution using the - * TeamCity format (for use with PhpStorm, for instance). - */ -class TeamCity extends ResultPrinter -{ - /** - * @var bool - */ - private $isSummaryTestCountPrinted = false; - - /** - * @var string - */ - private $startedTestName; - - /** - * @var false|int - */ - private $flowId; - - public function printResult(TestResult $result): void - { - $this->printHeader(); - $this->printFooter($result); - } - - /** - * An error occurred. - * - * @throws \InvalidArgumentException - */ - public function addError(Test $test, \Throwable $t, float $time): void - { - $this->printEvent( - 'testFailed', - [ - 'name' => $test->getName(), - 'message' => self::getMessage($t), - 'details' => self::getDetails($t), - 'duration' => self::toMilliseconds($time), - ] - ); - } - - /** - * A warning occurred. - * - * @throws \InvalidArgumentException - */ - public function addWarning(Test $test, Warning $e, float $time): void - { - $this->printEvent( - 'testFailed', - [ - 'name' => $test->getName(), - 'message' => self::getMessage($e), - 'details' => self::getDetails($e), - 'duration' => self::toMilliseconds($time), - ] - ); - } - - /** - * A failure occurred. - * - * @throws \InvalidArgumentException - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - $parameters = [ - 'name' => $test->getName(), - 'message' => self::getMessage($e), - 'details' => self::getDetails($e), - 'duration' => self::toMilliseconds($time), - ]; - - if ($e instanceof ExpectationFailedException) { - $comparisonFailure = $e->getComparisonFailure(); - - if ($comparisonFailure instanceof ComparisonFailure) { - $expectedString = $comparisonFailure->getExpectedAsString(); - - if ($expectedString === null || empty($expectedString)) { - $expectedString = self::getPrimitiveValueAsString($comparisonFailure->getExpected()); - } - - $actualString = $comparisonFailure->getActualAsString(); - - if ($actualString === null || empty($actualString)) { - $actualString = self::getPrimitiveValueAsString($comparisonFailure->getActual()); - } - - if ($actualString !== null && $expectedString !== null) { - $parameters['type'] = 'comparisonFailure'; - $parameters['actual'] = $actualString; - $parameters['expected'] = $expectedString; - } - } - } - - $this->printEvent('testFailed', $parameters); - } - - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - $this->printIgnoredTest($test->getName(), $t, $time); - } - - /** - * Risky test. - * - * @throws \InvalidArgumentException - */ - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - $this->addError($test, $t, $time); - } - - /** - * Skipped test. - * - * @throws \ReflectionException - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - $testName = $test->getName(); - - if ($this->startedTestName !== $testName) { - $this->startTest($test); - $this->printIgnoredTest($testName, $t, $time); - $this->endTest($test, $time); - } else { - $this->printIgnoredTest($testName, $t, $time); - } - } - - public function printIgnoredTest($testName, \Throwable $t, float $time): void - { - $this->printEvent( - 'testIgnored', - [ - 'name' => $testName, - 'message' => self::getMessage($t), - 'details' => self::getDetails($t), - 'duration' => self::toMilliseconds($time), - ] - ); - } - - /** - * A testsuite started. - * - * @throws \ReflectionException - */ - public function startTestSuite(TestSuite $suite): void - { - if (\stripos(\ini_get('disable_functions'), 'getmypid') === false) { - $this->flowId = \getmypid(); - } else { - $this->flowId = false; - } - - if (!$this->isSummaryTestCountPrinted) { - $this->isSummaryTestCountPrinted = true; - - $this->printEvent( - 'testCount', - ['count' => \count($suite)] - ); - } - - $suiteName = $suite->getName(); - - if (empty($suiteName)) { - return; - } - - $parameters = ['name' => $suiteName]; - - if (\class_exists($suiteName, false)) { - $fileName = self::getFileName($suiteName); - $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName"; - } else { - $split = \explode('::', $suiteName); - - if (\count($split) === 2 && \method_exists($split[0], $split[1])) { - $fileName = self::getFileName($split[0]); - $parameters['locationHint'] = "php_qn://$fileName::\\$suiteName"; - $parameters['name'] = $split[1]; - } - } - - $this->printEvent('testSuiteStarted', $parameters); - } - - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite): void - { - $suiteName = $suite->getName(); - - if (empty($suiteName)) { - return; - } - - $parameters = ['name' => $suiteName]; - - if (!\class_exists($suiteName, false)) { - $split = \explode('::', $suiteName); - - if (\count($split) === 2 && \method_exists($split[0], $split[1])) { - $parameters['name'] = $split[1]; - } - } - - $this->printEvent('testSuiteFinished', $parameters); - } - - /** - * A test started. - * - * @throws \ReflectionException - */ - public function startTest(Test $test): void - { - $testName = $test->getName(); - $this->startedTestName = $testName; - $params = ['name' => $testName]; - - if ($test instanceof TestCase) { - $className = \get_class($test); - $fileName = self::getFileName($className); - $params['locationHint'] = "php_qn://$fileName::\\$className::$testName"; - } - - $this->printEvent('testStarted', $params); - } - - /** - * A test ended. - */ - public function endTest(Test $test, float $time): void - { - parent::endTest($test, $time); - - $this->printEvent( - 'testFinished', - [ - 'name' => $test->getName(), - 'duration' => self::toMilliseconds($time), - ] - ); - } - - protected function writeProgress(string $progress): void - { - } - - /** - * @param string $eventName - * @param array $params - */ - private function printEvent($eventName, $params = []): void - { - $this->write("\n##teamcity[$eventName"); - - if ($this->flowId) { - $params['flowId'] = $this->flowId; - } - - foreach ($params as $key => $value) { - $escapedValue = self::escapeValue($value); - $this->write(" $key='$escapedValue'"); - } - - $this->write("]\n"); - } - - private static function getMessage(\Throwable $t): string - { - $message = ''; - - if ($t instanceof ExceptionWrapper) { - if ($t->getClassName() !== '') { - $message .= $t->getClassName(); - } - - if ($message !== '' && $t->getMessage() !== '') { - $message .= ' : '; - } - } - - return $message . $t->getMessage(); - } - - /** - * @throws \InvalidArgumentException - */ - private static function getDetails(\Throwable $t): string - { - $stackTrace = Filter::getFilteredStacktrace($t); - $previous = $t instanceof ExceptionWrapper ? $t->getPreviousWrapped() : $t->getPrevious(); - - while ($previous) { - $stackTrace .= "\nCaused by\n" . - TestFailure::exceptionToString($previous) . "\n" . - Filter::getFilteredStacktrace($previous); - - $previous = $previous instanceof ExceptionWrapper ? - $previous->getPreviousWrapped() : $previous->getPrevious(); - } - - return ' ' . \str_replace("\n", "\n ", $stackTrace); - } - - private static function getPrimitiveValueAsString($value): ?string - { - if ($value === null) { - return 'null'; - } - - if (\is_bool($value)) { - return $value === true ? 'true' : 'false'; - } - - if (\is_scalar($value)) { - return \print_r($value, true); - } - - return null; - } - - private static function escapeValue(string $text): string - { - return \str_replace( - ['|', "'", "\n", "\r", ']', '['], - ['||', "|'", '|n', '|r', '|]', '|['], - $text - ); - } - - /** - * @param string $className - * - * @throws \ReflectionException - */ - private static function getFileName($className): string - { - $reflectionClass = new ReflectionClass($className); - - return $reflectionClass->getFileName(); - } - - /** - * @param float $time microseconds - */ - private static function toMilliseconds(float $time): int - { - return \round($time * 1000); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util\Log; - -use DOMDocument; -use DOMElement; -use PHPUnit\Framework\AssertionFailedError; -use PHPUnit\Framework\ExceptionWrapper; -use PHPUnit\Framework\SelfDescribing; -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestFailure; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Framework\Warning; -use PHPUnit\Util\Filter; -use PHPUnit\Util\Printer; -use PHPUnit\Util\Xml; -use ReflectionClass; -use ReflectionException; - -/** - * A TestListener that generates a logfile of the test execution in XML markup. - * - * The XML markup used is the same as the one that is used by the JUnit Ant task. - */ -class JUnit extends Printer implements TestListener -{ - /** - * @var DOMDocument - */ - protected $document; - - /** - * @var DOMElement - */ - protected $root; - - /** - * @var bool - */ - protected $reportUselessTests = false; - - /** - * @var bool - */ - protected $writeDocument = true; - - /** - * @var DOMElement[] - */ - protected $testSuites = []; - - /** - * @var int[] - */ - protected $testSuiteTests = [0]; - - /** - * @var int[] - */ - protected $testSuiteAssertions = [0]; - - /** - * @var int[] - */ - protected $testSuiteErrors = [0]; - - /** - * @var int[] - */ - protected $testSuiteFailures = [0]; - - /** - * @var int[] - */ - protected $testSuiteSkipped = [0]; - - /** - * @var int[] - */ - protected $testSuiteTimes = [0]; - - /** - * @var int - */ - protected $testSuiteLevel = 0; - - /** - * @var DOMElement - */ - protected $currentTestCase; - - /** - * Constructor. - * - * @param null|mixed $out - * - * @throws \PHPUnit\Framework\Exception - */ - public function __construct($out = null, bool $reportUselessTests = false) - { - $this->document = new DOMDocument('1.0', 'UTF-8'); - $this->document->formatOutput = true; - - $this->root = $this->document->createElement('testsuites'); - $this->document->appendChild($this->root); - - parent::__construct($out); - - $this->reportUselessTests = $reportUselessTests; - } - - /** - * Flush buffer and close output. - */ - public function flush(): void - { - if ($this->writeDocument === true) { - $this->write($this->getXML()); - } - - parent::flush(); - } - - /** - * An error occurred. - * - * @throws \InvalidArgumentException - */ - public function addError(Test $test, \Throwable $t, float $time): void - { - $this->doAddFault($test, $t, $time, 'error'); - $this->testSuiteErrors[$this->testSuiteLevel]++; - } - - /** - * A warning occurred. - * - * @throws \InvalidArgumentException - */ - public function addWarning(Test $test, Warning $e, float $time): void - { - $this->doAddFault($test, $e, $time, 'warning'); - $this->testSuiteFailures[$this->testSuiteLevel]++; - } - - /** - * A failure occurred. - * - * @throws \InvalidArgumentException - */ - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - $this->doAddFault($test, $e, $time, 'failure'); - $this->testSuiteFailures[$this->testSuiteLevel]++; - } - - /** - * Incomplete test. - */ - public function addIncompleteTest(Test $test, \Throwable $t, float $time): void - { - $this->doAddSkipped($test); - } - - /** - * Risky test. - */ - public function addRiskyTest(Test $test, \Throwable $t, float $time): void - { - if (!$this->reportUselessTests || $this->currentTestCase === null) { - return; - } - - $error = $this->document->createElement( - 'error', - Xml::prepareString( - "Risky Test\n" . - Filter::getFilteredStacktrace($t) - ) - ); - - $error->setAttribute('type', \get_class($t)); - - $this->currentTestCase->appendChild($error); - - $this->testSuiteErrors[$this->testSuiteLevel]++; - } - - /** - * Skipped test. - */ - public function addSkippedTest(Test $test, \Throwable $t, float $time): void - { - $this->doAddSkipped($test); - } - - /** - * A testsuite started. - */ - public function startTestSuite(TestSuite $suite): void - { - $testSuite = $this->document->createElement('testsuite'); - $testSuite->setAttribute('name', $suite->getName()); - - if (\class_exists($suite->getName(), false)) { - try { - $class = new ReflectionClass($suite->getName()); - - $testSuite->setAttribute('file', $class->getFileName()); - } catch (ReflectionException $e) { - } - } - - if ($this->testSuiteLevel > 0) { - $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite); - } else { - $this->root->appendChild($testSuite); - } - - $this->testSuiteLevel++; - $this->testSuites[$this->testSuiteLevel] = $testSuite; - $this->testSuiteTests[$this->testSuiteLevel] = 0; - $this->testSuiteAssertions[$this->testSuiteLevel] = 0; - $this->testSuiteErrors[$this->testSuiteLevel] = 0; - $this->testSuiteFailures[$this->testSuiteLevel] = 0; - $this->testSuiteSkipped[$this->testSuiteLevel] = 0; - $this->testSuiteTimes[$this->testSuiteLevel] = 0; - } - - /** - * A testsuite ended. - */ - public function endTestSuite(TestSuite $suite): void - { - $this->testSuites[$this->testSuiteLevel]->setAttribute( - 'tests', - $this->testSuiteTests[$this->testSuiteLevel] - ); - - $this->testSuites[$this->testSuiteLevel]->setAttribute( - 'assertions', - $this->testSuiteAssertions[$this->testSuiteLevel] - ); - - $this->testSuites[$this->testSuiteLevel]->setAttribute( - 'errors', - $this->testSuiteErrors[$this->testSuiteLevel] - ); - - $this->testSuites[$this->testSuiteLevel]->setAttribute( - 'failures', - $this->testSuiteFailures[$this->testSuiteLevel] - ); - - $this->testSuites[$this->testSuiteLevel]->setAttribute( - 'skipped', - $this->testSuiteSkipped[$this->testSuiteLevel] - ); - - $this->testSuites[$this->testSuiteLevel]->setAttribute( - 'time', - \sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]) - ); - - if ($this->testSuiteLevel > 1) { - $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel]; - $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel]; - $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel]; - $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel]; - $this->testSuiteSkipped[$this->testSuiteLevel - 1] += $this->testSuiteSkipped[$this->testSuiteLevel]; - $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel]; - } - - $this->testSuiteLevel--; - } - - /** - * A test started. - * - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws ReflectionException - */ - public function startTest(Test $test): void - { - $usesDataprovider = false; - - if (\method_exists($test, 'usesDataProvider')) { - $usesDataprovider = $test->usesDataProvider(); - } - - $testCase = $this->document->createElement('testcase'); - $testCase->setAttribute('name', $test->getName()); - - $class = new ReflectionClass($test); - $methodName = $test->getName(!$usesDataprovider); - - if ($class->hasMethod($methodName)) { - $method = $class->getMethod($methodName); - - $testCase->setAttribute('class', $class->getName()); - $testCase->setAttribute('classname', \str_replace('\\', '.', $class->getName())); - $testCase->setAttribute('file', $class->getFileName()); - $testCase->setAttribute('line', $method->getStartLine()); - } - - $this->currentTestCase = $testCase; - } - - /** - * A test ended. - */ - public function endTest(Test $test, float $time): void - { - $numAssertions = 0; - - if (\method_exists($test, 'getNumAssertions')) { - $numAssertions = $test->getNumAssertions(); - } - - $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions; - - $this->currentTestCase->setAttribute( - 'assertions', - $numAssertions - ); - - $this->currentTestCase->setAttribute( - 'time', - \sprintf('%F', $time) - ); - - $this->testSuites[$this->testSuiteLevel]->appendChild( - $this->currentTestCase - ); - - $this->testSuiteTests[$this->testSuiteLevel]++; - $this->testSuiteTimes[$this->testSuiteLevel] += $time; - - $testOutput = ''; - - if (\method_exists($test, 'hasOutput') && \method_exists($test, 'getActualOutput')) { - $testOutput = $test->hasOutput() ? $test->getActualOutput() : ''; - } - - if (!empty($testOutput)) { - $systemOut = $this->document->createElement( - 'system-out', - Xml::prepareString($testOutput) - ); - - $this->currentTestCase->appendChild($systemOut); - } - - $this->currentTestCase = null; - } - - /** - * Returns the XML as a string. - */ - public function getXML(): string - { - return $this->document->saveXML(); - } - - /** - * Enables or disables the writing of the document - * in flush(). - * - * This is a "hack" needed for the integration of - * PHPUnit with Phing. - */ - public function setWriteDocument(/*bool*/ $flag): void - { - if (\is_bool($flag)) { - $this->writeDocument = $flag; - } - } - - /** - * Method which generalizes addError() and addFailure() - * - * @throws \InvalidArgumentException - */ - private function doAddFault(Test $test, \Throwable $t, float $time, $type): void - { - if ($this->currentTestCase === null) { - return; - } - - if ($test instanceof SelfDescribing) { - $buffer = $test->toString() . "\n"; - } else { - $buffer = ''; - } - - $buffer .= TestFailure::exceptionToString($t) . "\n" . - Filter::getFilteredStacktrace($t); - - $fault = $this->document->createElement( - $type, - Xml::prepareString($buffer) - ); - - if ($t instanceof ExceptionWrapper) { - $fault->setAttribute('type', $t->getClassName()); - } else { - $fault->setAttribute('type', \get_class($t)); - } - - $this->currentTestCase->appendChild($fault); - } - - private function doAddSkipped(Test $test): void - { - if ($this->currentTestCase === null) { - return; - } - - $skipped = $this->document->createElement('skipped'); - $this->currentTestCase->appendChild($skipped); - - $this->testSuiteSkipped[$this->testSuiteLevel]++; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -interface TestResultCacheInterface -{ - public function getState($testName): int; - - public function getTime($testName): float; - - public function load(): void; - - public function persist(): void; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use DOMElement; -use DOMXPath; -use PHPUnit\Framework\Exception; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\TestSuiteSorter; -use PHPUnit\TextUI\ResultPrinter; -use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; - -/** - * Wrapper for the PHPUnit XML configuration file. - * - * Example XML configuration file: - * - * - * - * - * - * - * /path/to/files - * /path/to/MyTest.php - * /path/to/files/exclude - * - * - * - * - * - * name - * - * - * name - * - * - * - * - * - * name - * - * - * name - * - * - * - * - * - * /path/to/files - * /path/to/file - * - * /path/to/files - * /path/to/file - * - * - * - * - * - * - * - * - * - * Sebastian - * - * - * 22 - * April - * 19.78 - * - * - * MyRelativeFile.php - * MyRelativeDir - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * . - * - * - * - * - * - * - * - * - * - * - * - * - * - */ -final class Configuration -{ - /** - * @var self[] - */ - private static $instances = []; - - /** - * @var \DOMDocument - */ - private $document; - - /** - * @var DOMXPath - */ - private $xpath; - - /** - * @var string - */ - private $filename; - - /** - * @var \LibXMLError[] - */ - private $errors = []; - - /** - * Returns a PHPUnit configuration object. - * - * @throws Exception - */ - public static function getInstance(string $filename): self - { - $realPath = \realpath($filename); - - if ($realPath === false) { - throw new Exception( - \sprintf( - 'Could not read "%s".', - $filename - ) - ); - } - - /** @var string $realPath */ - if (!isset(self::$instances[$realPath])) { - self::$instances[$realPath] = new self($realPath); - } - - return self::$instances[$realPath]; - } - - /** - * Loads a PHPUnit configuration file. - * - * @throws Exception - */ - private function __construct(string $filename) - { - $this->filename = $filename; - $this->document = Xml::loadFile($filename, false, true, true); - $this->xpath = new DOMXPath($this->document); - - $this->validateConfigurationAgainstSchema(); - } - - /** - * @codeCoverageIgnore - */ - private function __clone() - { - } - - public function hasValidationErrors(): bool - { - return \count($this->errors) > 0; - } - - public function getValidationErrors(): array - { - $result = []; - - foreach ($this->errors as $error) { - if (!isset($result[$error->line])) { - $result[$error->line] = []; - } - $result[$error->line][] = \trim($error->message); - } - - return $result; - } - - /** - * Returns the real path to the configuration file. - */ - public function getFilename(): string - { - return $this->filename; - } - - public function getExtensionConfiguration(): array - { - $result = []; - - foreach ($this->xpath->query('extensions/extension') as $extension) { - /** @var DOMElement $extension */ - $class = (string) $extension->getAttribute('class'); - $file = ''; - $arguments = $this->getConfigurationArguments($extension->childNodes); - - if ($extension->getAttribute('file')) { - $file = $this->toAbsolutePath( - (string) $extension->getAttribute('file'), - true - ); - } - $result[] = [ - 'class' => $class, - 'file' => $file, - 'arguments' => $arguments, - ]; - } - - return $result; - } - - /** - * Returns the configuration for SUT filtering. - */ - public function getFilterConfiguration(): array - { - $addUncoveredFilesFromWhitelist = true; - $processUncoveredFilesFromWhitelist = false; - $includeDirectory = []; - $includeFile = []; - $excludeDirectory = []; - $excludeFile = []; - - $tmp = $this->xpath->query('filter/whitelist'); - - if ($tmp->length === 1) { - if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { - $addUncoveredFilesFromWhitelist = $this->getBoolean( - (string) $tmp->item(0)->getAttribute( - 'addUncoveredFilesFromWhitelist' - ), - true - ); - } - - if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { - $processUncoveredFilesFromWhitelist = $this->getBoolean( - (string) $tmp->item(0)->getAttribute( - 'processUncoveredFilesFromWhitelist' - ), - false - ); - } - - $includeDirectory = $this->readFilterDirectories( - 'filter/whitelist/directory' - ); - - $includeFile = $this->readFilterFiles( - 'filter/whitelist/file' - ); - - $excludeDirectory = $this->readFilterDirectories( - 'filter/whitelist/exclude/directory' - ); - - $excludeFile = $this->readFilterFiles( - 'filter/whitelist/exclude/file' - ); - } - - return [ - 'whitelist' => [ - 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, - 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, - 'include' => [ - 'directory' => $includeDirectory, - 'file' => $includeFile, - ], - 'exclude' => [ - 'directory' => $excludeDirectory, - 'file' => $excludeFile, - ], - ], - ]; - } - - /** - * Returns the configuration for groups. - */ - public function getGroupConfiguration(): array - { - return $this->parseGroupConfiguration('groups'); - } - - /** - * Returns the configuration for testdox groups. - */ - public function getTestdoxGroupConfiguration(): array - { - return $this->parseGroupConfiguration('testdoxGroups'); - } - - /** - * Returns the configuration for listeners. - */ - public function getListenerConfiguration(): array - { - $result = []; - - foreach ($this->xpath->query('listeners/listener') as $listener) { - /** @var DOMElement $listener */ - $class = (string) $listener->getAttribute('class'); - $file = ''; - $arguments = $this->getConfigurationArguments($listener->childNodes); - - if ($listener->getAttribute('file')) { - $file = $this->toAbsolutePath( - (string) $listener->getAttribute('file'), - true - ); - } - - $result[] = [ - 'class' => $class, - 'file' => $file, - 'arguments' => $arguments, - ]; - } - - return $result; - } - - /** - * Returns the logging configuration. - */ - public function getLoggingConfiguration(): array - { - $result = []; - - foreach ($this->xpath->query('logging/log') as $log) { - /** @var DOMElement $log */ - $type = (string) $log->getAttribute('type'); - $target = (string) $log->getAttribute('target'); - - if (!$target) { - continue; - } - - $target = $this->toAbsolutePath($target); - - if ($type === 'coverage-html') { - if ($log->hasAttribute('lowUpperBound')) { - $result['lowUpperBound'] = $this->getInteger( - (string) $log->getAttribute('lowUpperBound'), - 50 - ); - } - - if ($log->hasAttribute('highLowerBound')) { - $result['highLowerBound'] = $this->getInteger( - (string) $log->getAttribute('highLowerBound'), - 90 - ); - } - } elseif ($type === 'coverage-crap4j') { - if ($log->hasAttribute('threshold')) { - $result['crap4jThreshold'] = $this->getInteger( - (string) $log->getAttribute('threshold'), - 30 - ); - } - } elseif ($type === 'coverage-text') { - if ($log->hasAttribute('showUncoveredFiles')) { - $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( - (string) $log->getAttribute('showUncoveredFiles'), - false - ); - } - - if ($log->hasAttribute('showOnlySummary')) { - $result['coverageTextShowOnlySummary'] = $this->getBoolean( - (string) $log->getAttribute('showOnlySummary'), - false - ); - } - } - - $result[$type] = $target; - } - - return $result; - } - - /** - * Returns the PHP configuration. - */ - public function getPHPConfiguration(): array - { - $result = [ - 'include_path' => [], - 'ini' => [], - 'const' => [], - 'var' => [], - 'env' => [], - 'post' => [], - 'get' => [], - 'cookie' => [], - 'server' => [], - 'files' => [], - 'request' => [], - ]; - - foreach ($this->xpath->query('php/includePath') as $includePath) { - $path = (string) $includePath->textContent; - - if ($path) { - $result['include_path'][] = $this->toAbsolutePath($path); - } - } - - foreach ($this->xpath->query('php/ini') as $ini) { - /** @var DOMElement $ini */ - $name = (string) $ini->getAttribute('name'); - $value = (string) $ini->getAttribute('value'); - - $result['ini'][$name]['value'] = $value; - } - - foreach ($this->xpath->query('php/const') as $const) { - /** @var DOMElement $const */ - $name = (string) $const->getAttribute('name'); - $value = (string) $const->getAttribute('value'); - - $result['const'][$name]['value'] = $this->getBoolean($value, $value); - } - - foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { - foreach ($this->xpath->query('php/' . $array) as $var) { - /** @var DOMElement $var */ - $name = (string) $var->getAttribute('name'); - $value = (string) $var->getAttribute('value'); - $verbatim = false; - - if ($var->hasAttribute('verbatim')) { - $verbatim = $this->getBoolean($var->getAttribute('verbatim'), false); - $result[$array][$name]['verbatim'] = $verbatim; - } - - if ($var->hasAttribute('force')) { - $force = $this->getBoolean($var->getAttribute('force'), false); - $result[$array][$name]['force'] = $force; - } - - if (!$verbatim) { - $value = $this->getBoolean($value, $value); - } - - $result[$array][$name]['value'] = $value; - } - } - - return $result; - } - - /** - * Handles the PHP configuration. - */ - public function handlePHPConfiguration(): void - { - $configuration = $this->getPHPConfiguration(); - - if (!empty($configuration['include_path'])) { - \ini_set( - 'include_path', - \implode(\PATH_SEPARATOR, $configuration['include_path']) . - \PATH_SEPARATOR . - \ini_get('include_path') - ); - } - - foreach ($configuration['ini'] as $name => $data) { - $value = $data['value']; - - if (\defined($value)) { - $value = (string) \constant($value); - } - - \ini_set($name, $value); - } - - foreach ($configuration['const'] as $name => $data) { - $value = $data['value']; - - if (!\defined($name)) { - \define($name, $value); - } - } - - foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { - /* - * @see https://github.com/sebastianbergmann/phpunit/issues/277 - */ - switch ($array) { - case 'var': - $target = &$GLOBALS; - - break; - - case 'server': - $target = &$_SERVER; - - break; - - default: - $target = &$GLOBALS['_' . \strtoupper($array)]; - - break; - } - - foreach ($configuration[$array] as $name => $data) { - $target[$name] = $data['value']; - } - } - - foreach ($configuration['env'] as $name => $data) { - $value = $data['value']; - $force = $data['force'] ?? false; - - if ($force || \getenv($name) === false) { - \putenv("{$name}={$value}"); - } - - $value = \getenv($name); - - if (!isset($_ENV[$name])) { - $_ENV[$name] = $value; - } - - if ($force === true) { - $_ENV[$name] = $value; - } - } - } - - /** - * Returns the PHPUnit configuration. - */ - public function getPHPUnitConfiguration(): array - { - $result = []; - $root = $this->document->documentElement; - - if ($root->hasAttribute('cacheTokens')) { - $result['cacheTokens'] = $this->getBoolean( - (string) $root->getAttribute('cacheTokens'), - false - ); - } - - if ($root->hasAttribute('columns')) { - $columns = (string) $root->getAttribute('columns'); - - if ($columns === 'max') { - $result['columns'] = 'max'; - } else { - $result['columns'] = $this->getInteger($columns, 80); - } - } - - if ($root->hasAttribute('colors')) { - /* only allow boolean for compatibility with previous versions - 'always' only allowed from command line */ - if ($this->getBoolean($root->getAttribute('colors'), false)) { - $result['colors'] = ResultPrinter::COLOR_AUTO; - } else { - $result['colors'] = ResultPrinter::COLOR_NEVER; - } - } - - /* - * @see https://github.com/sebastianbergmann/phpunit/issues/657 - */ - if ($root->hasAttribute('stderr')) { - $result['stderr'] = $this->getBoolean( - (string) $root->getAttribute('stderr'), - false - ); - } - - if ($root->hasAttribute('backupGlobals')) { - $result['backupGlobals'] = $this->getBoolean( - (string) $root->getAttribute('backupGlobals'), - false - ); - } - - if ($root->hasAttribute('backupStaticAttributes')) { - $result['backupStaticAttributes'] = $this->getBoolean( - (string) $root->getAttribute('backupStaticAttributes'), - false - ); - } - - if ($root->getAttribute('bootstrap')) { - $result['bootstrap'] = $this->toAbsolutePath( - (string) $root->getAttribute('bootstrap') - ); - } - - if ($root->hasAttribute('convertDeprecationsToExceptions')) { - $result['convertDeprecationsToExceptions'] = $this->getBoolean( - (string) $root->getAttribute('convertDeprecationsToExceptions'), - true - ); - } - - if ($root->hasAttribute('convertErrorsToExceptions')) { - $result['convertErrorsToExceptions'] = $this->getBoolean( - (string) $root->getAttribute('convertErrorsToExceptions'), - true - ); - } - - if ($root->hasAttribute('convertNoticesToExceptions')) { - $result['convertNoticesToExceptions'] = $this->getBoolean( - (string) $root->getAttribute('convertNoticesToExceptions'), - true - ); - } - - if ($root->hasAttribute('convertWarningsToExceptions')) { - $result['convertWarningsToExceptions'] = $this->getBoolean( - (string) $root->getAttribute('convertWarningsToExceptions'), - true - ); - } - - if ($root->hasAttribute('forceCoversAnnotation')) { - $result['forceCoversAnnotation'] = $this->getBoolean( - (string) $root->getAttribute('forceCoversAnnotation'), - false - ); - } - - if ($root->hasAttribute('disableCodeCoverageIgnore')) { - $result['disableCodeCoverageIgnore'] = $this->getBoolean( - (string) $root->getAttribute('disableCodeCoverageIgnore'), - false - ); - } - - if ($root->hasAttribute('processIsolation')) { - $result['processIsolation'] = $this->getBoolean( - (string) $root->getAttribute('processIsolation'), - false - ); - } - - if ($root->hasAttribute('stopOnDefect')) { - $result['stopOnDefect'] = $this->getBoolean( - (string) $root->getAttribute('stopOnDefect'), - false - ); - } - - if ($root->hasAttribute('stopOnError')) { - $result['stopOnError'] = $this->getBoolean( - (string) $root->getAttribute('stopOnError'), - false - ); - } - - if ($root->hasAttribute('stopOnFailure')) { - $result['stopOnFailure'] = $this->getBoolean( - (string) $root->getAttribute('stopOnFailure'), - false - ); - } - - if ($root->hasAttribute('stopOnWarning')) { - $result['stopOnWarning'] = $this->getBoolean( - (string) $root->getAttribute('stopOnWarning'), - false - ); - } - - if ($root->hasAttribute('stopOnIncomplete')) { - $result['stopOnIncomplete'] = $this->getBoolean( - (string) $root->getAttribute('stopOnIncomplete'), - false - ); - } - - if ($root->hasAttribute('stopOnRisky')) { - $result['stopOnRisky'] = $this->getBoolean( - (string) $root->getAttribute('stopOnRisky'), - false - ); - } - - if ($root->hasAttribute('stopOnSkipped')) { - $result['stopOnSkipped'] = $this->getBoolean( - (string) $root->getAttribute('stopOnSkipped'), - false - ); - } - - if ($root->hasAttribute('failOnWarning')) { - $result['failOnWarning'] = $this->getBoolean( - (string) $root->getAttribute('failOnWarning'), - false - ); - } - - if ($root->hasAttribute('failOnRisky')) { - $result['failOnRisky'] = $this->getBoolean( - (string) $root->getAttribute('failOnRisky'), - false - ); - } - - if ($root->hasAttribute('testSuiteLoaderClass')) { - $result['testSuiteLoaderClass'] = (string) $root->getAttribute( - 'testSuiteLoaderClass' - ); - } - - if ($root->hasAttribute('defaultTestSuite')) { - $result['defaultTestSuite'] = (string) $root->getAttribute( - 'defaultTestSuite' - ); - } - - if ($root->getAttribute('testSuiteLoaderFile')) { - $result['testSuiteLoaderFile'] = $this->toAbsolutePath( - (string) $root->getAttribute('testSuiteLoaderFile') - ); - } - - if ($root->hasAttribute('printerClass')) { - $result['printerClass'] = (string) $root->getAttribute( - 'printerClass' - ); - } - - if ($root->getAttribute('printerFile')) { - $result['printerFile'] = $this->toAbsolutePath( - (string) $root->getAttribute('printerFile') - ); - } - - if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) { - $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean( - (string) $root->getAttribute('beStrictAboutChangesToGlobalState'), - false - ); - } - - if ($root->hasAttribute('beStrictAboutOutputDuringTests')) { - $result['disallowTestOutput'] = $this->getBoolean( - (string) $root->getAttribute('beStrictAboutOutputDuringTests'), - false - ); - } - - if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) { - $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean( - (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'), - false - ); - } - - if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) { - $result['reportUselessTests'] = $this->getBoolean( - (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'), - true - ); - } - - if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { - $result['disallowTodoAnnotatedTests'] = $this->getBoolean( - (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'), - false - ); - } - - if ($root->hasAttribute('beStrictAboutCoversAnnotation')) { - $result['strictCoverage'] = $this->getBoolean( - (string) $root->getAttribute('beStrictAboutCoversAnnotation'), - false - ); - } - - if ($root->hasAttribute('defaultTimeLimit')) { - $result['defaultTimeLimit'] = $this->getInteger( - (string) $root->getAttribute('defaultTimeLimit'), - 1 - ); - } - - if ($root->hasAttribute('enforceTimeLimit')) { - $result['enforceTimeLimit'] = $this->getBoolean( - (string) $root->getAttribute('enforceTimeLimit'), - false - ); - } - - if ($root->hasAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage')) { - $result['ignoreDeprecatedCodeUnitsFromCodeCoverage'] = $this->getBoolean( - (string) $root->getAttribute('ignoreDeprecatedCodeUnitsFromCodeCoverage'), - false - ); - } - - if ($root->hasAttribute('timeoutForSmallTests')) { - $result['timeoutForSmallTests'] = $this->getInteger( - (string) $root->getAttribute('timeoutForSmallTests'), - 1 - ); - } - - if ($root->hasAttribute('timeoutForMediumTests')) { - $result['timeoutForMediumTests'] = $this->getInteger( - (string) $root->getAttribute('timeoutForMediumTests'), - 10 - ); - } - - if ($root->hasAttribute('timeoutForLargeTests')) { - $result['timeoutForLargeTests'] = $this->getInteger( - (string) $root->getAttribute('timeoutForLargeTests'), - 60 - ); - } - - if ($root->hasAttribute('reverseDefectList')) { - $result['reverseDefectList'] = $this->getBoolean( - (string) $root->getAttribute('reverseDefectList'), - false - ); - } - - if ($root->hasAttribute('verbose')) { - $result['verbose'] = $this->getBoolean( - (string) $root->getAttribute('verbose'), - false - ); - } - - if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) { - $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean( - (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'), - false - ); - } - - if ($root->hasAttribute('extensionsDirectory')) { - $result['extensionsDirectory'] = $this->toAbsolutePath( - (string) $root->getAttribute( - 'extensionsDirectory' - ) - ); - } - - if ($root->hasAttribute('cacheResult')) { - $result['cacheResult'] = $this->getBoolean( - (string) $root->getAttribute('cacheResult'), - false - ); - } - - if ($root->hasAttribute('cacheResultFile')) { - $result['cacheResultFile'] = $this->toAbsolutePath( - (string) $root->getAttribute('cacheResultFile') - ); - } - - if ($root->hasAttribute('executionOrder')) { - foreach (\explode(',', $root->getAttribute('executionOrder')) as $order) { - switch ($order) { - case 'default': - $result['executionOrder'] = TestSuiteSorter::ORDER_DEFAULT; - $result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT; - $result['resolveDependencies'] = false; - - break; - case 'reverse': - $result['executionOrder'] = TestSuiteSorter::ORDER_REVERSED; - - break; - case 'random': - $result['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED; - - break; - case 'defects': - $result['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST; - - break; - case 'depends': - $result['resolveDependencies'] = true; - - break; - } - } - } - - if ($root->hasAttribute('resolveDependencies')) { - $result['resolveDependencies'] = $this->getBoolean( - (string) $root->getAttribute('resolveDependencies'), - false - ); - } - - return $result; - } - - /** - * Returns the test suite configuration. - * - * @throws Exception - */ - public function getTestSuiteConfiguration(string $testSuiteFilter = ''): TestSuite - { - $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); - - if ($testSuiteNodes->length === 0) { - $testSuiteNodes = $this->xpath->query('testsuite'); - } - - if ($testSuiteNodes->length === 1) { - return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); - } - - $suite = new TestSuite; - - foreach ($testSuiteNodes as $testSuiteNode) { - $suite->addTestSuite( - $this->getTestSuite($testSuiteNode, $testSuiteFilter) - ); - } - - return $suite; - } - - /** - * Returns the test suite names from the configuration. - */ - public function getTestSuiteNames(): array - { - $names = []; - - foreach ($this->xpath->query('*/testsuite') as $node) { - /* @var DOMElement $node */ - $names[] = $node->getAttribute('name'); - } - - return $names; - } - - private function validateConfigurationAgainstSchema(): void - { - $original = \libxml_use_internal_errors(true); - $xsdFilename = __DIR__ . '/../../phpunit.xsd'; - - if (\defined('__PHPUNIT_PHAR_ROOT__')) { - $xsdFilename = __PHPUNIT_PHAR_ROOT__ . '/phpunit.xsd'; - } - - $this->document->schemaValidate($xsdFilename); - $this->errors = \libxml_get_errors(); - \libxml_clear_errors(); - \libxml_use_internal_errors($original); - } - - /** - * Collects and returns the configuration arguments from the PHPUnit - * XML configuration - */ - private function getConfigurationArguments(\DOMNodeList $nodes): array - { - $arguments = []; - - if ($nodes->length === 0) { - return $arguments; - } - - foreach ($nodes as $node) { - if (!$node instanceof DOMElement) { - continue; - } - - if ($node->tagName !== 'arguments') { - continue; - } - - foreach ($node->childNodes as $argument) { - if (!$argument instanceof DOMElement) { - continue; - } - - if ($argument->tagName === 'file' || $argument->tagName === 'directory') { - $arguments[] = $this->toAbsolutePath((string) $argument->textContent); - } else { - $arguments[] = Xml::xmlToVariable($argument); - } - } - } - - return $arguments; - } - - /** - * @throws \PHPUnit\Framework\Exception - */ - private function getTestSuite(DOMElement $testSuiteNode, string $testSuiteFilter = ''): TestSuite - { - if ($testSuiteNode->hasAttribute('name')) { - $suite = new TestSuite( - (string) $testSuiteNode->getAttribute('name') - ); - } else { - $suite = new TestSuite; - } - - $exclude = []; - - foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) { - $excludeFile = (string) $excludeNode->textContent; - - if ($excludeFile) { - $exclude[] = $this->toAbsolutePath($excludeFile); - } - } - - $fileIteratorFacade = new FileIteratorFacade; - $testSuiteFilter = $testSuiteFilter ? \explode(',', $testSuiteFilter) : []; - - foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) { - /** @var DOMElement $directoryNode */ - if (!empty($testSuiteFilter) && !\in_array($directoryNode->parentNode->getAttribute('name'), $testSuiteFilter)) { - continue; - } - - $directory = (string) $directoryNode->textContent; - - if (empty($directory)) { - continue; - } - - $prefix = ''; - $suffix = 'Test.php'; - - if (!$this->satisfiesPhpVersion($directoryNode)) { - continue; - } - - if ($directoryNode->hasAttribute('prefix')) { - $prefix = (string) $directoryNode->getAttribute('prefix'); - } - - if ($directoryNode->hasAttribute('suffix')) { - $suffix = (string) $directoryNode->getAttribute('suffix'); - } - - $files = $fileIteratorFacade->getFilesAsArray( - $this->toAbsolutePath($directory), - $suffix, - $prefix, - $exclude - ); - - $suite->addTestFiles($files); - } - - foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) { - /** @var DOMElement $fileNode */ - if (!empty($testSuiteFilter) && !\in_array($fileNode->parentNode->getAttribute('name'), $testSuiteFilter)) { - continue; - } - - $file = (string) $fileNode->textContent; - - if (empty($file)) { - continue; - } - - $file = $fileIteratorFacade->getFilesAsArray( - $this->toAbsolutePath($file) - ); - - if (!isset($file[0])) { - continue; - } - - $file = $file[0]; - - if (!$this->satisfiesPhpVersion($fileNode)) { - continue; - } - - $suite->addTestFile($file); - } - - return $suite; - } - - private function satisfiesPhpVersion(DOMElement $node): bool - { - $phpVersion = \PHP_VERSION; - $phpVersionOperator = '>='; - - if ($node->hasAttribute('phpVersion')) { - $phpVersion = (string) $node->getAttribute('phpVersion'); - } - - if ($node->hasAttribute('phpVersionOperator')) { - $phpVersionOperator = (string) $node->getAttribute('phpVersionOperator'); - } - - return \version_compare(\PHP_VERSION, $phpVersion, $phpVersionOperator); - } - - /** - * if $value is 'false' or 'true', this returns the value that $value represents. - * Otherwise, returns $default, which may be a string in rare cases. - * See PHPUnit\Util\ConfigurationTest::testPHPConfigurationIsReadCorrectly - * - * @param bool|string $default - * - * @return bool|string - */ - private function getBoolean(string $value, $default) - { - if (\strtolower($value) === 'false') { - return false; - } - - if (\strtolower($value) === 'true') { - return true; - } - - return $default; - } - - private function getInteger(string $value, int $default): int - { - if (\is_numeric($value)) { - return (int) $value; - } - - return $default; - } - - private function readFilterDirectories(string $query): array - { - $directories = []; - - foreach ($this->xpath->query($query) as $directoryNode) { - /** @var DOMElement $directoryNode */ - $directoryPath = (string) $directoryNode->textContent; - - if (!$directoryPath) { - continue; - } - - $prefix = ''; - $suffix = '.php'; - $group = 'DEFAULT'; - - if ($directoryNode->hasAttribute('prefix')) { - $prefix = (string) $directoryNode->getAttribute('prefix'); - } - - if ($directoryNode->hasAttribute('suffix')) { - $suffix = (string) $directoryNode->getAttribute('suffix'); - } - - if ($directoryNode->hasAttribute('group')) { - $group = (string) $directoryNode->getAttribute('group'); - } - - $directories[] = [ - 'path' => $this->toAbsolutePath($directoryPath), - 'prefix' => $prefix, - 'suffix' => $suffix, - 'group' => $group, - ]; - } - - return $directories; - } - - /** - * @return string[] - */ - private function readFilterFiles(string $query): array - { - $files = []; - - foreach ($this->xpath->query($query) as $file) { - $filePath = (string) $file->textContent; - - if ($filePath) { - $files[] = $this->toAbsolutePath($filePath); - } - } - - return $files; - } - - private function toAbsolutePath(string $path, bool $useIncludePath = false): string - { - $path = \trim($path); - - if ($path[0] === '/') { - return $path; - } - - // Matches the following on Windows: - // - \\NetworkComputer\Path - // - \\.\D: - // - \\.\c: - // - C:\Windows - // - C:\windows - // - C:/windows - // - c:/windows - if (\defined('PHP_WINDOWS_VERSION_BUILD') && - ($path[0] === '\\' || (\strlen($path) >= 3 && \preg_match('#^[A-Z]\:[/\\\]#i', \substr($path, 0, 3))))) { - return $path; - } - - if (\strpos($path, '://') !== false) { - return $path; - } - - $file = \dirname($this->filename) . \DIRECTORY_SEPARATOR . $path; - - if ($useIncludePath && !\file_exists($file)) { - $includePathFile = \stream_resolve_include_path($path); - - if ($includePathFile) { - $file = $includePathFile; - } - } - - return $file; - } - - private function parseGroupConfiguration(string $root): array - { - $groups = [ - 'include' => [], - 'exclude' => [], - ]; - - foreach ($this->xpath->query($root . '/include/group') as $group) { - $groups['include'][] = (string) $group->textContent; - } - - foreach ($this->xpath->query($root . '/exclude/group') as $group) { - $groups['exclude'][] = (string) $group->textContent; - } - - return $groups; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Exception; - -final class Json -{ - /** - * Prettify json string - * - * @throws \PHPUnit\Framework\Exception - */ - public static function prettify(string $json): string - { - $decodedJson = \json_decode($json, true); - - if (\json_last_error()) { - throw new Exception( - 'Cannot prettify invalid json' - ); - } - - return \json_encode($decodedJson, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES); - } - - /* - * To allow comparison of JSON strings, first process them into a consistent - * format so that they can be compared as strings. - * @return array ($error, $canonicalized_json) The $error parameter is used - * to indicate an error decoding the json. This is used to avoid ambiguity - * with JSON strings consisting entirely of 'null' or 'false'. - */ - public static function canonicalize(string $json): array - { - $decodedJson = \json_decode($json); - - if (\json_last_error()) { - return [true, null]; - } - - self::recursiveSort($decodedJson); - - $reencodedJson = \json_encode($decodedJson); - - return [false, $reencodedJson]; - } - - /* - * JSON object keys are unordered while PHP array keys are ordered. - * Sort all array keys to ensure both the expected and actual values have - * their keys in the same order. - */ - private static function recursiveSort(&$json): void - { - if (\is_array($json) === false) { - // If the object is not empty, change it to an associative array - // so we can sort the keys (and we will still re-encode it - // correctly, since PHP encodes associative arrays as JSON objects.) - // But EMPTY objects MUST remain empty objects. (Otherwise we will - // re-encode it as a JSON array rather than a JSON object.) - // See #2919. - if (\is_object($json) && \count((array) $json) > 0) { - $json = (array) $json; - } else { - return; - } - } - - \ksort($json); - - foreach ($json as $key => &$value) { - self::recursiveSort($value); - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestSuite; -use PHPUnit\Runner\PhptTestCase; - -final class TextTestListRenderer -{ - public function render(TestSuite $suite): string - { - $buffer = 'Available test(s):' . \PHP_EOL; - - foreach (new \RecursiveIteratorIterator($suite->getIterator()) as $test) { - if ($test instanceof TestCase) { - $name = \sprintf( - '%s::%s', - \get_class($test), - \str_replace(' with data set ', '', $test->getName()) - ); - } elseif ($test instanceof PhptTestCase) { - $name = $test->getName(); - } else { - continue; - } - - $buffer .= \sprintf( - ' - %s' . \PHP_EOL, - $name - ); - } - - return $buffer; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Runner; - -use PHPUnit\Framework\Test; -use PHPUnit\Util\Filesystem; - -class TestResultCache implements \Serializable, TestResultCacheInterface -{ - /** - * @var string - */ - public const DEFAULT_RESULT_CACHE_FILENAME = '.phpunit.result.cache'; - - /** - * Provide extra protection against incomplete or corrupt caches - * - * @var array - */ - private const ALLOWED_CACHE_TEST_STATUSES = [ - BaseTestRunner::STATUS_SKIPPED, - BaseTestRunner::STATUS_INCOMPLETE, - BaseTestRunner::STATUS_FAILURE, - BaseTestRunner::STATUS_ERROR, - BaseTestRunner::STATUS_RISKY, - BaseTestRunner::STATUS_WARNING, - ]; - - /** - * Path and filename for result cache file - * - * @var string - */ - private $cacheFilename; - - /** - * The list of defective tests - * - * - * // Mark a test skipped - * $this->defects[$testName] = BaseTestRunner::TEST_SKIPPED; - * - * - * @var array array - */ - private $defects = []; - - /** - * The list of execution duration of suites and tests (in seconds) - * - * - * // Record running time for test - * $this->times[$testName] = 1.234; - * - * - * @var array - */ - private $times = []; - - public function __construct($filename = null) - { - $this->cacheFilename = $filename ?? $_ENV['PHPUNIT_RESULT_CACHE'] ?? self::DEFAULT_RESULT_CACHE_FILENAME; - } - - public function persist(): void - { - $this->saveToFile(); - } - - public function saveToFile(): void - { - if (\defined('PHPUNIT_TESTSUITE_RESULTCACHE')) { - return; - } - - if (!Filesystem::createDirectory(\dirname($this->cacheFilename))) { - throw new Exception( - \sprintf( - 'Cannot create directory "%s" for result cache file', - $this->cacheFilename - ) - ); - } - - \file_put_contents( - $this->cacheFilename, - \serialize($this) - ); - } - - public function setState(string $testName, int $state): void - { - if ($state !== BaseTestRunner::STATUS_PASSED) { - $this->defects[$testName] = $state; - } - } - - public function getState($testName): int - { - return $this->defects[$testName] ?? BaseTestRunner::STATUS_UNKNOWN; - } - - public function setTime(string $testName, float $time): void - { - $this->times[$testName] = $time; - } - - public function getTime($testName): float - { - return $this->times[$testName] ?? 0; - } - - public function load(): void - { - $this->clear(); - - if (\is_file($this->cacheFilename) === false) { - return; - } - - $cacheData = @\file_get_contents($this->cacheFilename); - - // @codeCoverageIgnoreStart - if ($cacheData === false) { - return; - } - // @codeCoverageIgnoreEnd - - $cache = @\unserialize($cacheData, ['allowed_classes' => [self::class]]); - - if ($cache === false) { - return; - } - - if ($cache instanceof self) { - /* @var \PHPUnit\Runner\TestResultCache */ - $cache->copyStateToCache($this); - } - } - - public function copyStateToCache(self $targetCache): void - { - foreach ($this->defects as $name => $state) { - $targetCache->setState($name, $state); - } - - foreach ($this->times as $name => $time) { - $targetCache->setTime($name, $time); - } - } - - public function clear(): void - { - $this->defects = []; - $this->times = []; - } - - public function serialize(): string - { - return \serialize([ - 'defects' => $this->defects, - 'times' => $this->times, - ]); - } - - public function unserialize($serialized): void - { - $data = \unserialize($serialized); - - if (isset($data['times'])) { - foreach ($data['times'] as $testName => $testTime) { - $this->times[$testName] = (float) $testTime; - } - } - - if (isset($data['defects'])) { - foreach ($data['defects'] as $testName => $testResult) { - if (\in_array($testResult, self::ALLOWED_CACHE_TEST_STATUSES, true)) { - $this->defects[$testName] = $testResult; - } - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -use PHPUnit\Framework\Exception; - -/** - * Utility methods to load PHP sourcefiles. - */ -final class FileLoader -{ - /** - * Checks if a PHP sourcecode file is readable. The sourcecode file is loaded through the load() method. - * - * As a fallback, PHP looks in the directory of the file executing the stream_resolve_include_path function. - * We do not want to load the Test.php file here, so skip it if it found that. - * PHP prioritizes the include_path setting, so if the current directory is in there, it will first look in the - * current working directory. - * - * @throws Exception - */ - public static function checkAndLoad(string $filename): string - { - $includePathFilename = \stream_resolve_include_path($filename); - $localFile = __DIR__ . \DIRECTORY_SEPARATOR . $filename; - - /** - * @see https://github.com/sebastianbergmann/phpunit/pull/2751 - */ - $isReadable = @\fopen($includePathFilename, 'r') !== false; - - if (!$includePathFilename || !$isReadable || $includePathFilename === $localFile) { - throw new Exception( - \sprintf('Cannot open file "%s".' . "\n", $filename) - ); - } - - self::load($includePathFilename); - - return $includePathFilename; - } - - /** - * Loads a PHP sourcefile. - */ - public static function load(string $filename): void - { - $oldVariableNames = \array_keys(\get_defined_vars()); - - include_once $filename; - - $newVariables = \get_defined_vars(); - $newVariableNames = \array_diff(\array_keys($newVariables), $oldVariableNames); - - foreach ($newVariableNames as $variableName) { - if ($variableName !== 'oldVariableNames') { - $GLOBALS[$variableName] = $newVariables[$variableName]; - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace PHPUnit\Util; - -/** - * Filesystem helpers. - */ -final class Filesystem -{ - /** - * Maps class names to source file names: - * - PEAR CS: Foo_Bar_Baz -> Foo/Bar/Baz.php - * - Namespace: Foo\Bar\Baz -> Foo/Bar/Baz.php - */ - public static function classNameToFilename(string $className): string - { - return \str_replace( - ['_', '\\'], - \DIRECTORY_SEPARATOR, - $className - ) . '.php'; - } - - public static function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } -} -. - */ - -namespace Doctrine\Instantiator\Exception; - -use InvalidArgumentException as BaseInvalidArgumentException; -use ReflectionClass; - -/** - * Exception for invalid arguments provided to the instantiator - * - * @author Marco Pivetta - */ -class InvalidArgumentException extends BaseInvalidArgumentException implements ExceptionInterface -{ - public static function fromNonExistingClass(string $className) : self - { - if (interface_exists($className)) { - return new self(sprintf('The provided type "%s" is an interface, and can not be instantiated', $className)); - } - - if (PHP_VERSION_ID >= 50400 && trait_exists($className)) { - return new self(sprintf('The provided type "%s" is a trait, and can not be instantiated', $className)); - } - - return new self(sprintf('The provided class "%s" does not exist', $className)); - } - - public static function fromAbstractClass(ReflectionClass $reflectionClass) : self - { - return new self(sprintf( - 'The provided class "%s" is abstract, and can not be instantiated', - $reflectionClass->getName() - )); - } -} -. - */ - -namespace Doctrine\Instantiator\Exception; - -use Exception; -use ReflectionClass; -use UnexpectedValueException as BaseUnexpectedValueException; - -/** - * Exception for given parameters causing invalid/unexpected state on instantiation - * - * @author Marco Pivetta - */ -class UnexpectedValueException extends BaseUnexpectedValueException implements ExceptionInterface -{ - public static function fromSerializationTriggeredException( - ReflectionClass $reflectionClass, - Exception $exception - ) : self { - return new self( - sprintf( - 'An exception was raised while trying to instantiate an instance of "%s" via un-serialization', - $reflectionClass->getName() - ), - 0, - $exception - ); - } - - public static function fromUncleanUnSerialization( - ReflectionClass $reflectionClass, - string $errorString, - int $errorCode, - string $errorFile, - int $errorLine - ) : self { - return new self( - sprintf( - 'Could not produce an instance of "%s" via un-serialization, since an error was triggered ' - . 'in file "%s" at line "%d"', - $reflectionClass->getName(), - $errorFile, - $errorLine - ), - 0, - new Exception($errorString, $errorCode) - ); - } -} -. - */ - -namespace Doctrine\Instantiator\Exception; - -/** - * Base exception marker interface for the instantiator component - * - * @author Marco Pivetta - */ -interface ExceptionInterface -{ -} -. - */ - -namespace Doctrine\Instantiator; - -/** - * Instantiator provides utility methods to build objects without invoking their constructors - * - * @author Marco Pivetta - */ -interface InstantiatorInterface -{ - /** - * @param string $className - * - * @return object - * - * @throws \Doctrine\Instantiator\Exception\ExceptionInterface - */ - public function instantiate($className); -} -. - */ - -namespace Doctrine\Instantiator; - -use Doctrine\Instantiator\Exception\InvalidArgumentException; -use Doctrine\Instantiator\Exception\UnexpectedValueException; -use Exception; -use ReflectionClass; - -/** - * {@inheritDoc} - * - * @author Marco Pivetta - */ -final class Instantiator implements InstantiatorInterface -{ - /** - * Markers used internally by PHP to define whether {@see \unserialize} should invoke - * the method {@see \Serializable::unserialize()} when dealing with classes implementing - * the {@see \Serializable} interface. - */ - const SERIALIZATION_FORMAT_USE_UNSERIALIZER = 'C'; - const SERIALIZATION_FORMAT_AVOID_UNSERIALIZER = 'O'; - - /** - * @var \callable[] used to instantiate specific classes, indexed by class name - */ - private static $cachedInstantiators = []; - - /** - * @var object[] of objects that can directly be cloned, indexed by class name - */ - private static $cachedCloneables = []; - - /** - * {@inheritDoc} - */ - public function instantiate($className) - { - if (isset(self::$cachedCloneables[$className])) { - return clone self::$cachedCloneables[$className]; - } - - if (isset(self::$cachedInstantiators[$className])) { - $factory = self::$cachedInstantiators[$className]; - - return $factory(); - } - - return $this->buildAndCacheFromFactory($className); - } - - /** - * Builds the requested object and caches it in static properties for performance - * - * @return object - */ - private function buildAndCacheFromFactory(string $className) - { - $factory = self::$cachedInstantiators[$className] = $this->buildFactory($className); - $instance = $factory(); - - if ($this->isSafeToClone(new ReflectionClass($instance))) { - self::$cachedCloneables[$className] = clone $instance; - } - - return $instance; - } - - /** - * Builds a callable capable of instantiating the given $className without - * invoking its constructor. - * - * @throws InvalidArgumentException - * @throws UnexpectedValueException - * @throws \ReflectionException - */ - private function buildFactory(string $className) : callable - { - $reflectionClass = $this->getReflectionClass($className); - - if ($this->isInstantiableViaReflection($reflectionClass)) { - return [$reflectionClass, 'newInstanceWithoutConstructor']; - } - - $serializedString = sprintf( - '%s:%d:"%s":0:{}', - self::SERIALIZATION_FORMAT_AVOID_UNSERIALIZER, - strlen($className), - $className - ); - - $this->checkIfUnSerializationIsSupported($reflectionClass, $serializedString); - - return function () use ($serializedString) { - return unserialize($serializedString); - }; - } - - /** - * @param string $className - * - * @return ReflectionClass - * - * @throws InvalidArgumentException - * @throws \ReflectionException - */ - private function getReflectionClass($className) : ReflectionClass - { - if (! class_exists($className)) { - throw InvalidArgumentException::fromNonExistingClass($className); - } - - $reflection = new ReflectionClass($className); - - if ($reflection->isAbstract()) { - throw InvalidArgumentException::fromAbstractClass($reflection); - } - - return $reflection; - } - - /** - * @param ReflectionClass $reflectionClass - * @param string $serializedString - * - * @throws UnexpectedValueException - * - * @return void - */ - private function checkIfUnSerializationIsSupported(ReflectionClass $reflectionClass, $serializedString) : void - { - set_error_handler(function ($code, $message, $file, $line) use ($reflectionClass, & $error) : void { - $error = UnexpectedValueException::fromUncleanUnSerialization( - $reflectionClass, - $message, - $code, - $file, - $line - ); - }); - - $this->attemptInstantiationViaUnSerialization($reflectionClass, $serializedString); - - restore_error_handler(); - - if ($error) { - throw $error; - } - } - - /** - * @param ReflectionClass $reflectionClass - * @param string $serializedString - * - * @throws UnexpectedValueException - * - * @return void - */ - private function attemptInstantiationViaUnSerialization(ReflectionClass $reflectionClass, $serializedString) : void - { - try { - unserialize($serializedString); - } catch (Exception $exception) { - restore_error_handler(); - - throw UnexpectedValueException::fromSerializationTriggeredException($reflectionClass, $exception); - } - } - - private function isInstantiableViaReflection(ReflectionClass $reflectionClass) : bool - { - return ! ($this->hasInternalAncestors($reflectionClass) && $reflectionClass->isFinal()); - } - - /** - * Verifies whether the given class is to be considered internal - */ - private function hasInternalAncestors(ReflectionClass $reflectionClass) : bool - { - do { - if ($reflectionClass->isInternal()) { - return true; - } - } while ($reflectionClass = $reflectionClass->getParentClass()); - - return false; - } - - /** - * Checks if a class is cloneable - * - * Classes implementing `__clone` cannot be safely cloned, as that may cause side-effects. - */ - private function isSafeToClone(ReflectionClass $reflection) : bool - { - return $reflection->isCloneable() && ! $reflection->hasMethod('__clone'); - } -} -Copyright (c) 2014 Doctrine Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -/** - * The location where an element occurs within a file. - */ -final class Location -{ - /** @var int */ - private $lineNumber = 0; - - /** @var int */ - private $columnNumber = 0; - - /** - * Initializes the location for an element using its line number in the file and optionally the column number. - * - * @param int $lineNumber - * @param int $columnNumber - */ - public function __construct($lineNumber, $columnNumber = 0) - { - $this->lineNumber = $lineNumber; - $this->columnNumber = $columnNumber; - } - - /** - * Returns the line number that is covered by this location. - * - * @return integer - */ - public function getLineNumber() - { - return $this->lineNumber; - } - - /** - * Returns the column number (character position on a line) for this location object. - * - * @return integer - */ - public function getColumnNumber() - { - return $this->columnNumber; - } -} -fqsen = $fqsen; - - if (isset($matches[2])) { - $this->name = $matches[2]; - } else { - $matches = explode('\\', $fqsen); - $this->name = trim(end($matches), '()'); - } - } - - /** - * converts this class to string. - * - * @return string - */ - public function __toString() - { - return $this->fqsen; - } - - /** - * Returns the name of the element without path. - * - * @return string - */ - public function getName() - { - return $this->name; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -/** - * Interface for files processed by the ProjectFactory - */ -interface File -{ - /** - * Returns the content of the file as a string. - * - * @return string - */ - public function getContents(); - - /** - * Returns md5 hash of the file. - * - * @return string - */ - public function md5(); - - /** - * Returns an relative path to the file. - * - * @return string - */ - public function path(); -} -The MIT License (MIT) - -Copyright (c) 2015 phpDocumentor - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\GlobalState; - -/** - * Exports parts of a Snapshot as PHP code. - */ -class CodeExporter -{ - public function constants(Snapshot $snapshot): string - { - $result = ''; - - foreach ($snapshot->constants() as $name => $value) { - $result .= \sprintf( - 'if (!defined(\'%s\')) define(\'%s\', %s);' . "\n", - $name, - $name, - $this->exportVariable($value) - ); - } - - return $result; - } - - public function globalVariables(Snapshot $snapshot): string - { - $result = '$GLOBALS = [];' . PHP_EOL; - - foreach ($snapshot->globalVariables() as $name => $value) { - $result .= \sprintf( - '$GLOBALS[%s] = %s;' . PHP_EOL, - $this->exportVariable($name), - $this->exportVariable($value) - ); - } - - return $result; - } - - public function iniSettings(Snapshot $snapshot): string - { - $result = ''; - - foreach ($snapshot->iniSettings() as $key => $value) { - $result .= \sprintf( - '@ini_set(%s, %s);' . "\n", - $this->exportVariable($key), - $this->exportVariable($value) - ); - } - - return $result; - } - - private function exportVariable($variable): string - { - if (\is_scalar($variable) || \is_null($variable) || - (\is_array($variable) && $this->arrayOnlyContainsScalars($variable))) { - return \var_export($variable, true); - } - - return 'unserialize(' . \var_export(\serialize($variable), true) . ')'; - } - - private function arrayOnlyContainsScalars(array $array): bool - { - $result = true; - - foreach ($array as $element) { - if (\is_array($element)) { - $result = self::arrayOnlyContainsScalars($element); - } elseif (!\is_scalar($element) && !\is_null($element)) { - $result = false; - } - - if ($result === false) { - break; - } - } - - return $result; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\GlobalState; - -use ReflectionProperty; - -/** - * Restorer of snapshots of global state. - */ -class Restorer -{ - /** - * Deletes function definitions that are not defined in a snapshot. - * - * @throws RuntimeException when the uopz_delete() function is not available - * - * @see https://github.com/krakjoe/uopz - */ - public function restoreFunctions(Snapshot $snapshot) - { - if (!\function_exists('uopz_delete')) { - throw new RuntimeException('The uopz_delete() function is required for this operation'); - } - - $functions = \get_defined_functions(); - - foreach (\array_diff($functions['user'], $snapshot->functions()) as $function) { - uopz_delete($function); - } - } - - /** - * Restores all global and super-global variables from a snapshot. - */ - public function restoreGlobalVariables(Snapshot $snapshot) - { - $superGlobalArrays = $snapshot->superGlobalArrays(); - - foreach ($superGlobalArrays as $superGlobalArray) { - $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); - } - - $globalVariables = $snapshot->globalVariables(); - - foreach (\array_keys($GLOBALS) as $key) { - if ($key != 'GLOBALS' && - !\in_array($key, $superGlobalArrays) && - !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { - if (\array_key_exists($key, $globalVariables)) { - $GLOBALS[$key] = $globalVariables[$key]; - } else { - unset($GLOBALS[$key]); - } - } - } - } - - /** - * Restores all static attributes in user-defined classes from this snapshot. - */ - public function restoreStaticAttributes(Snapshot $snapshot) - { - $current = new Snapshot($snapshot->blacklist(), false, false, false, false, true, false, false, false, false); - $newClasses = \array_diff($current->classes(), $snapshot->classes()); - - unset($current); - - foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { - foreach ($staticAttributes as $name => $value) { - $reflector = new ReflectionProperty($className, $name); - $reflector->setAccessible(true); - $reflector->setValue($value); - } - } - - foreach ($newClasses as $className) { - $class = new \ReflectionClass($className); - $defaults = $class->getDefaultProperties(); - - foreach ($class->getProperties() as $attribute) { - if (!$attribute->isStatic()) { - continue; - } - - $name = $attribute->getName(); - - if ($snapshot->blacklist()->isStaticAttributeBlacklisted($className, $name)) { - continue; - } - - if (!isset($defaults[$name])) { - continue; - } - - $attribute->setAccessible(true); - $attribute->setValue($defaults[$name]); - } - } - } - - /** - * Restores a super-global variable array from this snapshot. - */ - private function restoreSuperGlobalArray(Snapshot $snapshot, string $superGlobalArray) - { - $superGlobalVariables = $snapshot->superGlobalVariables(); - - if (isset($GLOBALS[$superGlobalArray]) && - \is_array($GLOBALS[$superGlobalArray]) && - isset($superGlobalVariables[$superGlobalArray])) { - $keys = \array_keys( - \array_merge( - $GLOBALS[$superGlobalArray], - $superGlobalVariables[$superGlobalArray] - ) - ); - - foreach ($keys as $key) { - if (isset($superGlobalVariables[$superGlobalArray][$key])) { - $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; - } else { - unset($GLOBALS[$superGlobalArray][$key]); - } - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\GlobalState; - -use ReflectionClass; -use Serializable; - -/** - * A snapshot of global state. - */ -class Snapshot -{ - /** - * @var Blacklist - */ - private $blacklist; - - /** - * @var array - */ - private $globalVariables = []; - - /** - * @var array - */ - private $superGlobalArrays = []; - - /** - * @var array - */ - private $superGlobalVariables = []; - - /** - * @var array - */ - private $staticAttributes = []; - - /** - * @var array - */ - private $iniSettings = []; - - /** - * @var array - */ - private $includedFiles = []; - - /** - * @var array - */ - private $constants = []; - - /** - * @var array - */ - private $functions = []; - - /** - * @var array - */ - private $interfaces = []; - - /** - * @var array - */ - private $classes = []; - - /** - * @var array - */ - private $traits = []; - - /** - * Creates a snapshot of the current global state. - */ - public function __construct(Blacklist $blacklist = null, bool $includeGlobalVariables = true, bool $includeStaticAttributes = true, bool $includeConstants = true, bool $includeFunctions = true, bool $includeClasses = true, bool $includeInterfaces = true, bool $includeTraits = true, bool $includeIniSettings = true, bool $includeIncludedFiles = true) - { - if ($blacklist === null) { - $blacklist = new Blacklist; - } - - $this->blacklist = $blacklist; - - if ($includeConstants) { - $this->snapshotConstants(); - } - - if ($includeFunctions) { - $this->snapshotFunctions(); - } - - if ($includeClasses || $includeStaticAttributes) { - $this->snapshotClasses(); - } - - if ($includeInterfaces) { - $this->snapshotInterfaces(); - } - - if ($includeGlobalVariables) { - $this->setupSuperGlobalArrays(); - $this->snapshotGlobals(); - } - - if ($includeStaticAttributes) { - $this->snapshotStaticAttributes(); - } - - if ($includeIniSettings) { - $this->iniSettings = \ini_get_all(null, false); - } - - if ($includeIncludedFiles) { - $this->includedFiles = \get_included_files(); - } - - $this->traits = \get_declared_traits(); - } - - public function blacklist(): Blacklist - { - return $this->blacklist; - } - - public function globalVariables(): array - { - return $this->globalVariables; - } - - public function superGlobalVariables(): array - { - return $this->superGlobalVariables; - } - - public function superGlobalArrays(): array - { - return $this->superGlobalArrays; - } - - public function staticAttributes(): array - { - return $this->staticAttributes; - } - - public function iniSettings(): array - { - return $this->iniSettings; - } - - public function includedFiles(): array - { - return $this->includedFiles; - } - - public function constants(): array - { - return $this->constants; - } - - public function functions(): array - { - return $this->functions; - } - - public function interfaces(): array - { - return $this->interfaces; - } - - public function classes(): array - { - return $this->classes; - } - - public function traits(): array - { - return $this->traits; - } - - /** - * Creates a snapshot user-defined constants. - */ - private function snapshotConstants() - { - $constants = \get_defined_constants(true); - - if (isset($constants['user'])) { - $this->constants = $constants['user']; - } - } - - /** - * Creates a snapshot user-defined functions. - */ - private function snapshotFunctions() - { - $functions = \get_defined_functions(); - - $this->functions = $functions['user']; - } - - /** - * Creates a snapshot user-defined classes. - */ - private function snapshotClasses() - { - foreach (\array_reverse(\get_declared_classes()) as $className) { - $class = new ReflectionClass($className); - - if (!$class->isUserDefined()) { - break; - } - - $this->classes[] = $className; - } - - $this->classes = \array_reverse($this->classes); - } - - /** - * Creates a snapshot user-defined interfaces. - */ - private function snapshotInterfaces() - { - foreach (\array_reverse(\get_declared_interfaces()) as $interfaceName) { - $class = new ReflectionClass($interfaceName); - - if (!$class->isUserDefined()) { - break; - } - - $this->interfaces[] = $interfaceName; - } - - $this->interfaces = \array_reverse($this->interfaces); - } - - /** - * Creates a snapshot of all global and super-global variables. - */ - private function snapshotGlobals() - { - $superGlobalArrays = $this->superGlobalArrays(); - - foreach ($superGlobalArrays as $superGlobalArray) { - $this->snapshotSuperGlobalArray($superGlobalArray); - } - - foreach (\array_keys($GLOBALS) as $key) { - if ($key != 'GLOBALS' && - !\in_array($key, $superGlobalArrays) && - $this->canBeSerialized($GLOBALS[$key]) && - !$this->blacklist->isGlobalVariableBlacklisted($key)) { - $this->globalVariables[$key] = \unserialize(\serialize($GLOBALS[$key])); - } - } - } - - /** - * Creates a snapshot a super-global variable array. - */ - private function snapshotSuperGlobalArray(string $superGlobalArray) - { - $this->superGlobalVariables[$superGlobalArray] = []; - - if (isset($GLOBALS[$superGlobalArray]) && \is_array($GLOBALS[$superGlobalArray])) { - foreach ($GLOBALS[$superGlobalArray] as $key => $value) { - $this->superGlobalVariables[$superGlobalArray][$key] = \unserialize(\serialize($value)); - } - } - } - - /** - * Creates a snapshot of all static attributes in user-defined classes. - */ - private function snapshotStaticAttributes() - { - foreach ($this->classes as $className) { - $class = new ReflectionClass($className); - $snapshot = []; - - foreach ($class->getProperties() as $attribute) { - if ($attribute->isStatic()) { - $name = $attribute->getName(); - - if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { - continue; - } - - $attribute->setAccessible(true); - $value = $attribute->getValue(); - - if ($this->canBeSerialized($value)) { - $snapshot[$name] = \unserialize(\serialize($value)); - } - } - } - - if (!empty($snapshot)) { - $this->staticAttributes[$className] = $snapshot; - } - } - } - - /** - * Returns a list of all super-global variable arrays. - */ - private function setupSuperGlobalArrays() - { - $this->superGlobalArrays = [ - '_ENV', - '_POST', - '_GET', - '_COOKIE', - '_SERVER', - '_FILES', - '_REQUEST' - ]; - - if (\ini_get('register_long_arrays') == '1') { - $this->superGlobalArrays = \array_merge( - $this->superGlobalArrays, - [ - 'HTTP_ENV_VARS', - 'HTTP_POST_VARS', - 'HTTP_GET_VARS', - 'HTTP_COOKIE_VARS', - 'HTTP_SERVER_VARS', - 'HTTP_POST_FILES' - ] - ); - } - } - - /** - * @todo Implement this properly - */ - private function canBeSerialized($variable): bool - { - if (!\is_object($variable)) { - return !\is_resource($variable); - } - - if ($variable instanceof \stdClass) { - return true; - } - - $class = new ReflectionClass($variable); - - do { - if ($class->isInternal()) { - return $variable instanceof Serializable; - } - } while ($class = $class->getParentClass()); - - return true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\GlobalState; - -use ReflectionClass; - -/** - * A blacklist for global state elements that should not be snapshotted. - */ -class Blacklist -{ - /** - * @var array - */ - private $globalVariables = []; - - /** - * @var string[] - */ - private $classes = []; - - /** - * @var string[] - */ - private $classNamePrefixes = []; - - /** - * @var string[] - */ - private $parentClasses = []; - - /** - * @var string[] - */ - private $interfaces = []; - - /** - * @var array - */ - private $staticAttributes = []; - - public function addGlobalVariable(string $variableName) - { - $this->globalVariables[$variableName] = true; - } - - public function addClass(string $className) - { - $this->classes[] = $className; - } - - public function addSubclassesOf(string $className) - { - $this->parentClasses[] = $className; - } - - public function addImplementorsOf(string $interfaceName) - { - $this->interfaces[] = $interfaceName; - } - - public function addClassNamePrefix(string $classNamePrefix) - { - $this->classNamePrefixes[] = $classNamePrefix; - } - - public function addStaticAttribute(string $className, string $attributeName) - { - if (!isset($this->staticAttributes[$className])) { - $this->staticAttributes[$className] = []; - } - - $this->staticAttributes[$className][$attributeName] = true; - } - - public function isGlobalVariableBlacklisted(string $variableName): bool - { - return isset($this->globalVariables[$variableName]); - } - - public function isStaticAttributeBlacklisted(string $className, string $attributeName): bool - { - if (\in_array($className, $this->classes)) { - return true; - } - - foreach ($this->classNamePrefixes as $prefix) { - if (\strpos($className, $prefix) === 0) { - return true; - } - } - - $class = new ReflectionClass($className); - - foreach ($this->parentClasses as $type) { - if ($class->isSubclassOf($type)) { - return true; - } - } - - foreach ($this->interfaces as $type) { - if ($class->implementsInterface($type)) { - return true; - } - } - - if (isset($this->staticAttributes[$className][$attributeName])) { - return true; - } - - return false; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\GlobalState; - -class RuntimeException extends \RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\GlobalState; - -interface Exception -{ -} -sebastian/global-state - -Copyright (c) 2001-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -callback = $callable; - } - - /** - * {@inheritdoc} - */ - public function apply($element) - { - return call_user_func($this->callback, $element); - } -} - $propertyValue) { - $copy->{$propertyName} = $propertyValue; - } - - return $copy; - } -} -copier = $copier; - } - - /** - * {@inheritdoc} - */ - public function apply($element) - { - $newElement = clone $element; - - $copy = $this->createCopyClosure(); - - return $copy($newElement); - } - - private function createCopyClosure() - { - $copier = $this->copier; - - $copy = function (SplDoublyLinkedList $list) use ($copier) { - // Replace each element in the list with a deep copy of itself - for ($i = 1; $i <= $list->count(); $i++) { - $copy = $copier->recursiveCopy($list->shift()); - - $list->push($copy); - } - - return $list; - }; - - return Closure::bind($copy, null, DeepCopy::class); - } -} -getProperties() does not return private properties from ancestor classes. - * - * @author muratyaman@gmail.com - * @see http://php.net/manual/en/reflectionclass.getproperties.php - * - * @param ReflectionClass $ref - * - * @return ReflectionProperty[] - */ - public static function getProperties(ReflectionClass $ref) - { - $props = $ref->getProperties(); - $propsArr = array(); - - foreach ($props as $prop) { - $propertyName = $prop->getName(); - $propsArr[$propertyName] = $prop; - } - - if ($parentClass = $ref->getParentClass()) { - $parentPropsArr = self::getProperties($parentClass); - foreach ($propsArr as $key => $property) { - $parentPropsArr[$key] = $property; - } - - return $parentPropsArr; - } - - return $propsArr; - } - - /** - * Retrieves property by name from object and all its ancestors. - * - * @param object|string $object - * @param string $name - * - * @throws PropertyException - * @throws ReflectionException - * - * @return ReflectionProperty - */ - public static function getProperty($object, $name) - { - $reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object); - - if ($reflection->hasProperty($name)) { - return $reflection->getProperty($name); - } - - if ($parentClass = $reflection->getParentClass()) { - return self::getProperty($parentClass->getName(), $name); - } - - throw new PropertyException( - sprintf( - 'The class "%s" doesn\'t have a property with the given name: "%s".', - is_object($object) ? get_class($object) : $object, - $name - ) - ); - } -} -type = $type; - } - - /** - * @param mixed $element - * - * @return boolean - */ - public function matches($element) - { - return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type; - } -} -property = $property; - } - - /** - * Matches a property by its name. - * - * {@inheritdoc} - */ - public function matches($object, $property) - { - return $property == $this->property; - } -} -propertyType = $propertyType; - } - - /** - * {@inheritdoc} - */ - public function matches($object, $property) - { - try { - $reflectionProperty = ReflectionHelper::getProperty($object, $property); - } catch (ReflectionException $exception) { - return false; - } - - $reflectionProperty->setAccessible(true); - - return $reflectionProperty->getValue($object) instanceof $this->propertyType; - } -} -class = $class; - $this->property = $property; - } - - /** - * Matches a specific property of a specific class. - * - * {@inheritdoc} - */ - public function matches($object, $property) - { - return ($object instanceof $this->class) && $property == $this->property; - } -} -callback = $callable; - } - - /** - * Replaces the object property by the result of the callback called with the object property. - * - * {@inheritdoc} - */ - public function apply($object, $property, $objectCopier) - { - $reflectionProperty = ReflectionHelper::getProperty($object, $property); - $reflectionProperty->setAccessible(true); - - $value = call_user_func($this->callback, $reflectionProperty->getValue($object)); - - $reflectionProperty->setValue($object, $value); - } -} -setAccessible(true); - - $reflectionProperty->setValue($object, new ArrayCollection()); - } -} setAccessible(true); - $oldCollection = $reflectionProperty->getValue($object); - - $newCollection = $oldCollection->map( - function ($item) use ($objectCopier) { - return $objectCopier($item); - } - ); - - $reflectionProperty->setValue($object, $newCollection); - } -} -__load(); - } -} -setAccessible(true); - $reflectionProperty->setValue($object, null); - } -} - Filter, 'matcher' => Matcher] pairs. - */ - private $filters = []; - - /** - * Type Filters to apply. - * - * @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs. - */ - private $typeFilters = []; - - /** - * @var bool - */ - private $skipUncloneable = false; - - /** - * @var bool - */ - private $useCloneMethod; - - /** - * @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used - * instead of the regular deep cloning. - */ - public function __construct($useCloneMethod = false) - { - $this->useCloneMethod = $useCloneMethod; - - $this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class)); - $this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class)); - } - - /** - * If enabled, will not throw an exception when coming across an uncloneable property. - * - * @param $skipUncloneable - * - * @return $this - */ - public function skipUncloneable($skipUncloneable = true) - { - $this->skipUncloneable = $skipUncloneable; - - return $this; - } - - /** - * Deep copies the given object. - * - * @param mixed $object - * - * @return mixed - */ - public function copy($object) - { - $this->hashMap = []; - - return $this->recursiveCopy($object); - } - - public function addFilter(Filter $filter, Matcher $matcher) - { - $this->filters[] = [ - 'matcher' => $matcher, - 'filter' => $filter, - ]; - } - - public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher) - { - $this->typeFilters[] = [ - 'matcher' => $matcher, - 'filter' => $filter, - ]; - } - - private function recursiveCopy($var) - { - // Matches Type Filter - if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) { - return $filter->apply($var); - } - - // Resource - if (is_resource($var)) { - return $var; - } - - // Array - if (is_array($var)) { - return $this->copyArray($var); - } - - // Scalar - if (! is_object($var)) { - return $var; - } - - // Object - return $this->copyObject($var); - } - - /** - * Copy an array - * @param array $array - * @return array - */ - private function copyArray(array $array) - { - foreach ($array as $key => $value) { - $array[$key] = $this->recursiveCopy($value); - } - - return $array; - } - - /** - * Copies an object. - * - * @param object $object - * - * @throws CloneException - * - * @return object - */ - private function copyObject($object) - { - $objectHash = spl_object_hash($object); - - if (isset($this->hashMap[$objectHash])) { - return $this->hashMap[$objectHash]; - } - - $reflectedObject = new ReflectionObject($object); - $isCloneable = $reflectedObject->isCloneable(); - - if (false === $isCloneable) { - if ($this->skipUncloneable) { - $this->hashMap[$objectHash] = $object; - - return $object; - } - - throw new CloneException( - sprintf( - 'The class "%s" is not cloneable.', - $reflectedObject->getName() - ) - ); - } - - $newObject = clone $object; - $this->hashMap[$objectHash] = $newObject; - - if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) { - return $newObject; - } - - if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) { - return $newObject; - } - - foreach (ReflectionHelper::getProperties($reflectedObject) as $property) { - $this->copyObjectProperty($newObject, $property); - } - - return $newObject; - } - - private function copyObjectProperty($object, ReflectionProperty $property) - { - // Ignore static properties - if ($property->isStatic()) { - return; - } - - // Apply the filters - foreach ($this->filters as $item) { - /** @var Matcher $matcher */ - $matcher = $item['matcher']; - /** @var Filter $filter */ - $filter = $item['filter']; - - if ($matcher->matches($object, $property->getName())) { - $filter->apply( - $object, - $property->getName(), - function ($object) { - return $this->recursiveCopy($object); - } - ); - - // If a filter matches, we stop processing this property - return; - } - } - - $property->setAccessible(true); - $propertyValue = $property->getValue($object); - - // Copy the property - $property->setValue($object, $this->recursiveCopy($propertyValue)); - } - - /** - * Returns first filter that matches variable, `null` if no such filter found. - * - * @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and - * 'matcher' with value of type {@see TypeMatcher} - * @param mixed $var - * - * @return TypeFilter|null - */ - private function getFirstMatchedTypeFilter(array $filterRecords, $var) - { - $matched = $this->first( - $filterRecords, - function (array $record) use ($var) { - /* @var TypeMatcher $matcher */ - $matcher = $record['matcher']; - - return $matcher->matches($var); - } - ); - - return isset($matched) ? $matched['filter'] : null; - } - - /** - * Returns first element that matches predicate, `null` if no such element found. - * - * @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs. - * @param callable $predicate Predicate arguments are: element. - * - * @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher' - * with value of type {@see TypeMatcher} or `null`. - */ - private function first(array $elements, callable $predicate) - { - foreach ($elements as $element) { - if (call_user_func($predicate, $element)) { - return $element; - } - } - - return null; - } -} -copy($value); - } -} -The MIT License (MIT) - -Copyright (c) 2013 My C-Sense - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -phpunit/phpunit: 7.5.6 -doctrine/instantiator: 1.1.0 -myclabs/deep-copy: 1.8.1 -phar-io/manifest: 1.0.3 -phar-io/version: 2.0.1 -phpdocumentor/reflection-common: 1.0.1 -phpdocumentor/reflection-docblock: 4.3.0 -phpdocumentor/type-resolver: 0.4.0 -phpspec/prophecy: 1.8.0 -phpunit/php-code-coverage: 6.1.4 -phpunit/php-file-iterator: 2.0.2 -phpunit/php-invoker: 2.0.0 -phpunit/php-text-template: 1.2.1 -phpunit/php-timer: 2.0.0 -phpunit/php-token-stream: 3.0.1 -sebastian/code-unit-reverse-lookup: 1.0.1 -sebastian/comparator: 3.0.2 -sebastian/diff: 3.0.2 -sebastian/environment: 4.1.0 -sebastian/exporter: 3.1.0 -sebastian/global-state: 2.0.0 -sebastian/object-enumerator: 3.0.3 -sebastian/object-reflector: 1.1.1 -sebastian/recursion-context: 3.0.0 -sebastian/resource-operations: 2.0.1 -sebastian/version: 2.0.1 -symfony/polyfill-ctype: v1.10.0 -theseer/tokenizer: 1.1.0 -webmozart/assert: 1.4.0 - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\ObjectReflector; - -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\ObjectReflector; - -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -declare(strict_types=1); - -namespace SebastianBergmann\ObjectReflector; - -class ObjectReflector -{ - /** - * @param object $object - * - * @return array - * - * @throws InvalidArgumentException - */ - public function getAttributes($object): array - { - if (!is_object($object)) { - throw new InvalidArgumentException; - } - - $attributes = []; - $className = get_class($object); - - foreach ((array) $object as $name => $value) { - $name = explode("\0", (string) $name); - - if (count($name) === 1) { - $name = $name[0]; - } else { - if ($name[1] !== $className) { - $name = $name[1] . '::' . $name[2]; - } else { - $name = $name[2]; - } - } - - $attributes[$name] = $value; - } - - return $attributes; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Invoker; - -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Invoker; - -final class Invoker -{ - /** - * @var int - */ - private $timeout; - - /** - * @throws \Throwable - */ - public function invoke(callable $callable, array $arguments, int $timeout) - { - \pcntl_signal( - \SIGALRM, - function (): void { - throw new TimeoutException( - \sprintf( - 'Execution aborted after %d second%s', - $this->timeout, - $this->timeout === 1 ? '' : 's' - ) - ); - }, - true - ); - - $this->timeout = $timeout; - - \pcntl_async_signals(true); - \pcntl_alarm($timeout); - - try { - $result = \call_user_func_array($callable, $arguments); - } catch (\Throwable $t) { - \pcntl_alarm(0); - - throw $t; - } - - \pcntl_alarm(0); - - return $result; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Invoker; - -final class TimeoutException extends \RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Timer; - -final class Timer -{ - /** - * @var array - */ - private static $times = [ - 'hour' => 3600000, - 'minute' => 60000, - 'second' => 1000 - ]; - - /** - * @var array - */ - private static $startTimes = []; - - public static function start(): void - { - self::$startTimes[] = \microtime(true); - } - - public static function stop(): float - { - return \microtime(true) - \array_pop(self::$startTimes); - } - - public static function secondsToTimeString(float $time): string - { - $ms = \round($time * 1000); - - foreach (self::$times as $unit => $value) { - if ($ms >= $value) { - $time = \floor($ms / $value * 100.0) / 100.0; - - return $time . ' ' . ($time == 1 ? $unit : $unit . 's'); - } - } - - return $ms . ' ms'; - } - - /** - * @throws RuntimeException - */ - public static function timeSinceStartOfRequest(): string - { - if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { - $startOfRequest = $_SERVER['REQUEST_TIME_FLOAT']; - } elseif (isset($_SERVER['REQUEST_TIME'])) { - $startOfRequest = $_SERVER['REQUEST_TIME']; - } else { - throw new RuntimeException('Cannot determine time at which the request started'); - } - - return self::secondsToTimeString(\microtime(true) - $startOfRequest); - } - - /** - * @throws RuntimeException - */ - public static function resourceUsage(): string - { - return \sprintf( - 'Time: %s, Memory: %4.2fMB', - self::timeSinceStartOfRequest(), - \memory_get_peak_usage(true) / 1048576 - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Timer; - -final class RuntimeException extends \RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Timer; - -interface Exception -{ -} -phpunit/php-timer - -Copyright (c) 2010-2018, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\CodeUnitReverseLookup; - -/** - * @since Class available since Release 1.0.0 - */ -class Wizard -{ - /** - * @var array - */ - private $lookupTable = []; - - /** - * @var array - */ - private $processedClasses = []; - - /** - * @var array - */ - private $processedFunctions = []; - - /** - * @param string $filename - * @param int $lineNumber - * - * @return string - */ - public function lookup($filename, $lineNumber) - { - if (!isset($this->lookupTable[$filename][$lineNumber])) { - $this->updateLookupTable(); - } - - if (isset($this->lookupTable[$filename][$lineNumber])) { - return $this->lookupTable[$filename][$lineNumber]; - } else { - return $filename . ':' . $lineNumber; - } - } - - private function updateLookupTable() - { - $this->processClassesAndTraits(); - $this->processFunctions(); - } - - private function processClassesAndTraits() - { - foreach (array_merge(get_declared_classes(), get_declared_traits()) as $classOrTrait) { - if (isset($this->processedClasses[$classOrTrait])) { - continue; - } - - $reflector = new \ReflectionClass($classOrTrait); - - foreach ($reflector->getMethods() as $method) { - $this->processFunctionOrMethod($method); - } - - $this->processedClasses[$classOrTrait] = true; - } - } - - private function processFunctions() - { - foreach (get_defined_functions()['user'] as $function) { - if (isset($this->processedFunctions[$function])) { - continue; - } - - $this->processFunctionOrMethod(new \ReflectionFunction($function)); - - $this->processedFunctions[$function] = true; - } - } - - /** - * @param \ReflectionFunctionAbstract $functionOrMethod - */ - private function processFunctionOrMethod(\ReflectionFunctionAbstract $functionOrMethod) - { - if ($functionOrMethod->isInternal()) { - return; - } - - $name = $functionOrMethod->getName(); - - if ($functionOrMethod instanceof \ReflectionMethod) { - $name = $functionOrMethod->getDeclaringClass()->getName() . '::' . $name; - } - - if (!isset($this->lookupTable[$functionOrMethod->getFileName()])) { - $this->lookupTable[$functionOrMethod->getFileName()] = []; - } - - foreach (range($functionOrMethod->getStartLine(), $functionOrMethod->getEndLine()) as $line) { - $this->lookupTable[$functionOrMethod->getFileName()][$line] = $name; - } - } -} -code-unit-reverse-lookup - -Copyright (c) 2016-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -use phpDocumentor\Reflection\DocBlock\Tag; -use Webmozart\Assert\Assert; - -final class DocBlock -{ - /** @var string The opening line for this docblock. */ - private $summary = ''; - - /** @var DocBlock\Description The actual description for this docblock. */ - private $description = null; - - /** @var Tag[] An array containing all the tags in this docblock; except inline. */ - private $tags = []; - - /** @var Types\Context Information about the context of this DocBlock. */ - private $context = null; - - /** @var Location Information about the location of this DocBlock. */ - private $location = null; - - /** @var bool Is this DocBlock (the start of) a template? */ - private $isTemplateStart = false; - - /** @var bool Does this DocBlock signify the end of a DocBlock template? */ - private $isTemplateEnd = false; - - /** - * @param string $summary - * @param DocBlock\Description $description - * @param DocBlock\Tag[] $tags - * @param Types\Context $context The context in which the DocBlock occurs. - * @param Location $location The location within the file that this DocBlock occurs in. - * @param bool $isTemplateStart - * @param bool $isTemplateEnd - */ - public function __construct( - $summary = '', - DocBlock\Description $description = null, - array $tags = [], - Types\Context $context = null, - Location $location = null, - $isTemplateStart = false, - $isTemplateEnd = false - ) { - Assert::string($summary); - Assert::boolean($isTemplateStart); - Assert::boolean($isTemplateEnd); - Assert::allIsInstanceOf($tags, Tag::class); - - $this->summary = $summary; - $this->description = $description ?: new DocBlock\Description(''); - foreach ($tags as $tag) { - $this->addTag($tag); - } - - $this->context = $context; - $this->location = $location; - - $this->isTemplateEnd = $isTemplateEnd; - $this->isTemplateStart = $isTemplateStart; - } - - /** - * @return string - */ - public function getSummary() - { - return $this->summary; - } - - /** - * @return DocBlock\Description - */ - public function getDescription() - { - return $this->description; - } - - /** - * Returns the current context. - * - * @return Types\Context - */ - public function getContext() - { - return $this->context; - } - - /** - * Returns the current location. - * - * @return Location - */ - public function getLocation() - { - return $this->location; - } - - /** - * Returns whether this DocBlock is the start of a Template section. - * - * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker - * (`#@+`) that is appended directly after the opening `/**` of a DocBlock. - * - * An example of such an opening is: - * - * ``` - * /**#@+ - * * My DocBlock - * * / - * ``` - * - * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all - * elements that follow until another DocBlock is found that contains the closing marker (`#@-`). - * - * @see self::isTemplateEnd() for the check whether a closing marker was provided. - * - * @return boolean - */ - public function isTemplateStart() - { - return $this->isTemplateStart; - } - - /** - * Returns whether this DocBlock is the end of a Template section. - * - * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality. - * - * @return boolean - */ - public function isTemplateEnd() - { - return $this->isTemplateEnd; - } - - /** - * Returns the tags for this DocBlock. - * - * @return Tag[] - */ - public function getTags() - { - return $this->tags; - } - - /** - * Returns an array of tags matching the given name. If no tags are found - * an empty array is returned. - * - * @param string $name String to search by. - * - * @return Tag[] - */ - public function getTagsByName($name) - { - Assert::string($name); - - $result = []; - - /** @var Tag $tag */ - foreach ($this->getTags() as $tag) { - if ($tag->getName() !== $name) { - continue; - } - - $result[] = $tag; - } - - return $result; - } - - /** - * Checks if a tag of a certain type is present in this DocBlock. - * - * @param string $name Tag name to check for. - * - * @return bool - */ - public function hasTag($name) - { - Assert::string($name); - - /** @var Tag $tag */ - foreach ($this->getTags() as $tag) { - if ($tag->getName() === $name) { - return true; - } - } - - return false; - } - - /** - * Remove a tag from this DocBlock. - * - * @param Tag $tag The tag to remove. - * - * @return void - */ - public function removeTag(Tag $tagToRemove) - { - foreach ($this->tags as $key => $tag) { - if ($tag === $tagToRemove) { - unset($this->tags[$key]); - break; - } - } - } - - /** - * Adds a tag to this DocBlock. - * - * @param Tag $tag The tag to add. - * - * @return void - */ - private function addTag(Tag $tag) - { - $this->tags[] = $tag; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection; - -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\DocBlock\StandardTagFactory; -use phpDocumentor\Reflection\DocBlock\Tag; -use phpDocumentor\Reflection\DocBlock\TagFactory; -use Webmozart\Assert\Assert; - -final class DocBlockFactory implements DocBlockFactoryInterface -{ - /** @var DocBlock\DescriptionFactory */ - private $descriptionFactory; - - /** @var DocBlock\TagFactory */ - private $tagFactory; - - /** - * Initializes this factory with the required subcontractors. - * - * @param DescriptionFactory $descriptionFactory - * @param TagFactory $tagFactory - */ - public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory) - { - $this->descriptionFactory = $descriptionFactory; - $this->tagFactory = $tagFactory; - } - - /** - * Factory method for easy instantiation. - * - * @param string[] $additionalTags - * - * @return DocBlockFactory - */ - public static function createInstance(array $additionalTags = []) - { - $fqsenResolver = new FqsenResolver(); - $tagFactory = new StandardTagFactory($fqsenResolver); - $descriptionFactory = new DescriptionFactory($tagFactory); - - $tagFactory->addService($descriptionFactory); - $tagFactory->addService(new TypeResolver($fqsenResolver)); - - $docBlockFactory = new self($descriptionFactory, $tagFactory); - foreach ($additionalTags as $tagName => $tagHandler) { - $docBlockFactory->registerTagHandler($tagName, $tagHandler); - } - - return $docBlockFactory; - } - - /** - * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the - * getDocComment method (such as a ReflectionClass object). - * @param Types\Context $context - * @param Location $location - * - * @return DocBlock - */ - public function create($docblock, Types\Context $context = null, Location $location = null) - { - if (is_object($docblock)) { - if (!method_exists($docblock, 'getDocComment')) { - $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method'; - throw new \InvalidArgumentException($exceptionMessage); - } - - $docblock = $docblock->getDocComment(); - } - - Assert::stringNotEmpty($docblock); - - if ($context === null) { - $context = new Types\Context(''); - } - - $parts = $this->splitDocBlock($this->stripDocComment($docblock)); - list($templateMarker, $summary, $description, $tags) = $parts; - - return new DocBlock( - $summary, - $description ? $this->descriptionFactory->create($description, $context) : null, - array_filter($this->parseTagBlock($tags, $context), function ($tag) { - return $tag instanceof Tag; - }), - $context, - $location, - $templateMarker === '#@+', - $templateMarker === '#@-' - ); - } - - public function registerTagHandler($tagName, $handler) - { - $this->tagFactory->registerTagHandler($tagName, $handler); - } - - /** - * Strips the asterisks from the DocBlock comment. - * - * @param string $comment String containing the comment text. - * - * @return string - */ - private function stripDocComment($comment) - { - $comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment)); - - // reg ex above is not able to remove */ from a single line docblock - if (substr($comment, -2) === '*/') { - $comment = trim(substr($comment, 0, -2)); - } - - return str_replace(["\r\n", "\r"], "\n", $comment); - } - - /** - * Splits the DocBlock into a template marker, summary, description and block of tags. - * - * @param string $comment Comment to split into the sub-parts. - * - * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split. - * @author Mike van Riel for extending the regex with template marker support. - * - * @return string[] containing the template marker (if any), summary, description and a string containing the tags. - */ - private function splitDocBlock($comment) - { - // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This - // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the - // performance impact of running a regular expression - if (strpos($comment, '@') === 0) { - return ['', '', '', $comment]; - } - - // clears all extra horizontal whitespace from the line endings to prevent parsing issues - $comment = preg_replace('/\h*$/Sum', '', $comment); - - /* - * Splits the docblock into a template marker, summary, description and tags section. - * - * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may - * occur after it and will be stripped). - * - The short description is started from the first character until a dot is encountered followed by a - * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing - * errors). This is optional. - * - The long description, any character until a new line is encountered followed by an @ and word - * characters (a tag). This is optional. - * - Tags; the remaining characters - * - * Big thanks to RichardJ for contributing this Regular Expression - */ - preg_match( - '/ - \A - # 1. Extract the template marker - (?:(\#\@\+|\#\@\-)\n?)? - - # 2. Extract the summary - (?: - (?! @\pL ) # The summary may not start with an @ - ( - [^\n.]+ - (?: - (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines - [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line - [^\n.]+ # Include anything else - )* - \.? - )? - ) - - # 3. Extract the description - (?: - \s* # Some form of whitespace _must_ precede a description because a summary must be there - (?! @\pL ) # The description may not start with an @ - ( - [^\n]+ - (?: \n+ - (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line - [^\n]+ # Include anything else - )* - ) - )? - - # 4. Extract the tags (anything that follows) - (\s+ [\s\S]*)? # everything that follows - /ux', - $comment, - $matches - ); - array_shift($matches); - - while (count($matches) < 4) { - $matches[] = ''; - } - - return $matches; - } - - /** - * Creates the tag objects. - * - * @param string $tags Tag block to parse. - * @param Types\Context $context Context of the parsed Tag - * - * @return DocBlock\Tag[] - */ - private function parseTagBlock($tags, Types\Context $context) - { - $tags = $this->filterTagBlock($tags); - if (!$tags) { - return []; - } - - $result = $this->splitTagBlockIntoTagLines($tags); - foreach ($result as $key => $tagLine) { - $result[$key] = $this->tagFactory->create(trim($tagLine), $context); - } - - return $result; - } - - /** - * @param string $tags - * - * @return string[] - */ - private function splitTagBlockIntoTagLines($tags) - { - $result = []; - foreach (explode("\n", $tags) as $tag_line) { - if (isset($tag_line[0]) && ($tag_line[0] === '@')) { - $result[] = $tag_line; - } else { - $result[count($result) - 1] .= "\n" . $tag_line; - } - } - - return $result; - } - - /** - * @param $tags - * @return string - */ - private function filterTagBlock($tags) - { - $tags = trim($tags); - if (!$tags) { - return null; - } - - if ('@' !== $tags[0]) { - // @codeCoverageIgnoreStart - // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that - // we didn't foresee. - throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags); - // @codeCoverageIgnoreEnd - } - - return $tags; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock\Tags\Example; - -/** - * Class used to find an example file's location based on a given ExampleDescriptor. - */ -class ExampleFinder -{ - /** @var string */ - private $sourceDirectory = ''; - - /** @var string[] */ - private $exampleDirectories = []; - - /** - * Attempts to find the example contents for the given descriptor. - * - * @param Example $example - * - * @return string - */ - public function find(Example $example) - { - $filename = $example->getFilePath(); - - $file = $this->getExampleFileContents($filename); - if (!$file) { - return "** File not found : {$filename} **"; - } - - return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount())); - } - - /** - * Registers the project's root directory where an 'examples' folder can be expected. - * - * @param string $directory - * - * @return void - */ - public function setSourceDirectory($directory = '') - { - $this->sourceDirectory = $directory; - } - - /** - * Returns the project's root directory where an 'examples' folder can be expected. - * - * @return string - */ - public function getSourceDirectory() - { - return $this->sourceDirectory; - } - - /** - * Registers a series of directories that may contain examples. - * - * @param string[] $directories - */ - public function setExampleDirectories(array $directories) - { - $this->exampleDirectories = $directories; - } - - /** - * Returns a series of directories that may contain examples. - * - * @return string[] - */ - public function getExampleDirectories() - { - return $this->exampleDirectories; - } - - /** - * Attempts to find the requested example file and returns its contents or null if no file was found. - * - * This method will try several methods in search of the given example file, the first one it encounters is - * returned: - * - * 1. Iterates through all examples folders for the given filename - * 2. Checks the source folder for the given filename - * 3. Checks the 'examples' folder in the current working directory for examples - * 4. Checks the path relative to the current working directory for the given filename - * - * @param string $filename - * - * @return string|null - */ - private function getExampleFileContents($filename) - { - $normalizedPath = null; - - foreach ($this->exampleDirectories as $directory) { - $exampleFileFromConfig = $this->constructExamplePath($directory, $filename); - if (is_readable($exampleFileFromConfig)) { - $normalizedPath = $exampleFileFromConfig; - break; - } - } - - if (!$normalizedPath) { - if (is_readable($this->getExamplePathFromSource($filename))) { - $normalizedPath = $this->getExamplePathFromSource($filename); - } elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) { - $normalizedPath = $this->getExamplePathFromExampleDirectory($filename); - } elseif (is_readable($filename)) { - $normalizedPath = $filename; - } - } - - return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null; - } - - /** - * Get example filepath based on the example directory inside your project. - * - * @param string $file - * - * @return string - */ - private function getExamplePathFromExampleDirectory($file) - { - return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file; - } - - /** - * Returns a path to the example file in the given directory.. - * - * @param string $directory - * @param string $file - * - * @return string - */ - private function constructExamplePath($directory, $file) - { - return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file; - } - - /** - * Get example filepath based on sourcecode. - * - * @param string $file - * - * @return string - */ - private function getExamplePathFromSource($file) - { - return sprintf( - '%s%s%s', - trim($this->getSourceDirectory(), '\\/'), - DIRECTORY_SEPARATOR, - trim($file, '"') - ); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock\Tags\Formatter; - -interface Tag -{ - public function getName(); - - public static function create($body); - - public function render(Formatter $formatter = null); - - public function __toString(); -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\Types\Context as TypeContext; - -/** - * Creates a new Description object given a body of text. - * - * Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their - * body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the - * Description object's `render` method. - * - * In addition to the above does a Description support two types of escape sequences: - * - * 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}` - * 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description - * of an inline tag. - * - * If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning - * of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping - * over unexpected spaces as can be observed with tag descriptions. - */ -class DescriptionFactory -{ - /** @var TagFactory */ - private $tagFactory; - - /** - * Initializes this factory with the means to construct (inline) tags. - * - * @param TagFactory $tagFactory - */ - public function __construct(TagFactory $tagFactory) - { - $this->tagFactory = $tagFactory; - } - - /** - * Returns the parsed text of this description. - * - * @param string $contents - * @param TypeContext $context - * - * @return Description - */ - public function create($contents, TypeContext $context = null) - { - list($text, $tags) = $this->parse($this->lex($contents), $context); - - return new Description($text, $tags); - } - - /** - * Strips the contents from superfluous whitespace and splits the description into a series of tokens. - * - * @param string $contents - * - * @return string[] A series of tokens of which the description text is composed. - */ - private function lex($contents) - { - $contents = $this->removeSuperfluousStartingWhitespace($contents); - - // performance optimalization; if there is no inline tag, don't bother splitting it up. - if (strpos($contents, '{@') === false) { - return [$contents]; - } - - return preg_split( - '/\{ - # "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally. - (?!@\}) - # We want to capture the whole tag line, but without the inline tag delimiters. - (\@ - # Match everything up to the next delimiter. - [^{}]* - # Nested inline tag content should not be captured, or it will appear in the result separately. - (?: - # Match nested inline tags. - (?: - # Because we did not catch the tag delimiters earlier, we must be explicit with them here. - # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. - \{(?1)?\} - | - # Make sure we match hanging "{". - \{ - ) - # Match content after the nested inline tag. - [^{}]* - )* # If there are more inline tags, match them as well. We use "*" since there may not be any - # nested inline tags. - ) - \}/Sux', - $contents, - null, - PREG_SPLIT_DELIM_CAPTURE - ); - } - - /** - * Parses the stream of tokens in to a new set of tokens containing Tags. - * - * @param string[] $tokens - * @param TypeContext $context - * - * @return string[]|Tag[] - */ - private function parse($tokens, TypeContext $context) - { - $count = count($tokens); - $tagCount = 0; - $tags = []; - - for ($i = 1; $i < $count; $i += 2) { - $tags[] = $this->tagFactory->create($tokens[$i], $context); - $tokens[$i] = '%' . ++$tagCount . '$s'; - } - - //In order to allow "literal" inline tags, the otherwise invalid - //sequence "{@}" is changed to "@", and "{}" is changed to "}". - //"%" is escaped to "%%" because of vsprintf. - //See unit tests for examples. - for ($i = 0; $i < $count; $i += 2) { - $tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]); - } - - return [implode('', $tokens), $tags]; - } - - /** - * Removes the superfluous from a multi-line description. - * - * When a description has more than one line then it can happen that the second and subsequent lines have an - * additional indentation. This is commonly in use with tags like this: - * - * {@}since 1.1.0 This is an example - * description where we have an - * indentation in the second and - * subsequent lines. - * - * If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent - * lines and this may cause rendering issues when, for example, using a Markdown converter. - * - * @param string $contents - * - * @return string - */ - private function removeSuperfluousStartingWhitespace($contents) - { - $lines = explode("\n", $contents); - - // if there is only one line then we don't have lines with superfluous whitespace and - // can use the contents as-is - if (count($lines) <= 1) { - return $contents; - } - - // determine how many whitespace characters need to be stripped - $startingSpaceCount = 9999999; - for ($i = 1; $i < count($lines); $i++) { - // lines with a no length do not count as they are not indented at all - if (strlen(trim($lines[$i])) === 0) { - continue; - } - - // determine the number of prefixing spaces by checking the difference in line length before and after - // an ltrim - $startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i]))); - } - - // strip the number of spaces from each line - if ($startingSpaceCount > 0) { - for ($i = 1; $i < count($lines); $i++) { - $lines[$i] = substr($lines[$i], $startingSpaceCount); - } - } - - return implode("\n", $lines); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock\Tags\Formatter; -use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter; -use Webmozart\Assert\Assert; - -/** - * Object representing to description for a DocBlock. - * - * A Description object can consist of plain text but can also include tags. A Description Formatter can then combine - * a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete - * description text using the format that you would prefer. - * - * Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is - * thus recommended to use that to create a Description object, like this: - * - * $description = $descriptionFactory->create('This is a {@see Description}', $context); - * - * The description factory will interpret the given body and create a body template and list of tags from them, and pass - * that onto the constructor if this class. - * - * > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace - * > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial - * > type names and FQSENs. - * - * If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this: - * - * $description = new Description( - * 'This is a %1$s', - * [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ] - * ); - * - * It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object - * is mainly responsible for rendering. - * - * @see DescriptionFactory to create a new Description. - * @see Description\Formatter for the formatting of the body and tags. - */ -class Description -{ - /** @var string */ - private $bodyTemplate; - - /** @var Tag[] */ - private $tags; - - /** - * Initializes a Description with its body (template) and a listing of the tags used in the body template. - * - * @param string $bodyTemplate - * @param Tag[] $tags - */ - public function __construct($bodyTemplate, array $tags = []) - { - Assert::string($bodyTemplate); - - $this->bodyTemplate = $bodyTemplate; - $this->tags = $tags; - } - - /** - * Returns the tags for this DocBlock. - * - * @return Tag[] - */ - public function getTags() - { - return $this->tags; - } - - /** - * Renders this description as a string where the provided formatter will format the tags in the expected string - * format. - * - * @param Formatter|null $formatter - * - * @return string - */ - public function render(Formatter $formatter = null) - { - if ($formatter === null) { - $formatter = new PassthroughFormatter(); - } - - $tags = []; - foreach ($this->tags as $tag) { - $tags[] = '{' . $formatter->format($tag) . '}'; - } - - return vsprintf($this->bodyTemplate, $tags); - } - - /** - * Returns a plain string representation of this description. - * - * @return string - */ - public function __toString() - { - return $this->render(); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock; -use Webmozart\Assert\Assert; - -/** - * Converts a DocBlock back from an object to a complete DocComment including Asterisks. - */ -class Serializer -{ - /** @var string The string to indent the comment with. */ - protected $indentString = ' '; - - /** @var int The number of times the indent string is repeated. */ - protected $indent = 0; - - /** @var bool Whether to indent the first line with the given indent amount and string. */ - protected $isFirstLineIndented = true; - - /** @var int|null The max length of a line. */ - protected $lineLength = null; - - /** @var DocBlock\Tags\Formatter A custom tag formatter. */ - protected $tagFormatter = null; - - /** - * Create a Serializer instance. - * - * @param int $indent The number of times the indent string is repeated. - * @param string $indentString The string to indent the comment with. - * @param bool $indentFirstLine Whether to indent the first line. - * @param int|null $lineLength The max length of a line or NULL to disable line wrapping. - * @param DocBlock\Tags\Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter. - */ - public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null, $tagFormatter = null) - { - Assert::integer($indent); - Assert::string($indentString); - Assert::boolean($indentFirstLine); - Assert::nullOrInteger($lineLength); - Assert::nullOrIsInstanceOf($tagFormatter, 'phpDocumentor\Reflection\DocBlock\Tags\Formatter'); - - $this->indent = $indent; - $this->indentString = $indentString; - $this->isFirstLineIndented = $indentFirstLine; - $this->lineLength = $lineLength; - $this->tagFormatter = $tagFormatter ?: new DocBlock\Tags\Formatter\PassthroughFormatter(); - } - - /** - * Generate a DocBlock comment. - * - * @param DocBlock $docblock The DocBlock to serialize. - * - * @return string The serialized doc block. - */ - public function getDocComment(DocBlock $docblock) - { - $indent = str_repeat($this->indentString, $this->indent); - $firstIndent = $this->isFirstLineIndented ? $indent : ''; - // 3 === strlen(' * ') - $wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null; - - $text = $this->removeTrailingSpaces( - $indent, - $this->addAsterisksForEachLine( - $indent, - $this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength) - ) - ); - - $comment = "{$firstIndent}/**\n"; - if ($text) { - $comment .= "{$indent} * {$text}\n"; - $comment .= "{$indent} *\n"; - } - - $comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment); - $comment .= $indent . ' */'; - - return $comment; - } - - /** - * @param $indent - * @param $text - * @return mixed - */ - private function removeTrailingSpaces($indent, $text) - { - return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text); - } - - /** - * @param $indent - * @param $text - * @return mixed - */ - private function addAsterisksForEachLine($indent, $text) - { - return str_replace("\n", "\n{$indent} * ", $text); - } - - /** - * @param DocBlock $docblock - * @param $wrapLength - * @return string - */ - private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength) - { - $text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription() - : ''); - if ($wrapLength !== null) { - $text = wordwrap($text, $wrapLength); - return $text; - } - - return $text; - } - - /** - * @param DocBlock $docblock - * @param $wrapLength - * @param $indent - * @param $comment - * @return string - */ - private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment) - { - foreach ($docblock->getTags() as $tag) { - $tagText = $this->tagFormatter->format($tag); - if ($wrapLength !== null) { - $tagText = wordwrap($tagText, $wrapLength); - } - - $tagText = str_replace("\n", "\n{$indent} * ", $tagText); - - $comment .= "{$indent} * {$tagText}\n"; - } - - return $comment; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\Types\Context as TypeContext; - -interface TagFactory -{ - /** - * Adds a parameter to the service locator that can be injected in a tag's factory method. - * - * When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to - * typehint a parameter in the signature so that we can use that interface or class name to inject a dependency - * (see {@see addService()} for more information on that). - * - * Another way is to check the name of the argument against the names in the Service Locator. With this method - * you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching - * name. - * - * Be aware that there are two reserved names: - * - * - name, representing the name of the tag. - * - body, representing the complete body of the tag. - * - * These parameters are injected at the last moment and will override any existing parameter with those names. - * - * @param string $name - * @param mixed $value - * - * @return void - */ - public function addParameter($name, $value); - - /** - * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided. - * - * When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter - * has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint. - * - * Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the - * interface is passed as alias then every time that interface is requested the provided service will be returned. - * - * @param object $service - * @param string $alias - * - * @return void - */ - public function addService($service); - - /** - * Factory method responsible for instantiating the correct sub type. - * - * @param string $tagLine The text for this tag, including description. - * @param TypeContext $context - * - * @throws \InvalidArgumentException if an invalid tag line was presented. - * - * @return Tag A new tag object. - */ - public function create($tagLine, TypeContext $context = null); - - /** - * Registers a handler for tags. - * - * If you want to use your own tags then you can use this method to instruct the TagFactory to register the name - * of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement the {@see Tag} interface (and thus - * the create method). - * - * @param string $tagName Name of tag to register a handler for. When registering a namespaced tag, the full - * name, along with a prefixing slash MUST be provided. - * @param string $handler FQCN of handler. - * - * @throws \InvalidArgumentException if the tag name is not a string - * @throws \InvalidArgumentException if the tag name is namespaced (contains backslashes) but does not start with - * a backslash - * @throws \InvalidArgumentException if the handler is not a string - * @throws \InvalidArgumentException if the handler is not an existing class - * @throws \InvalidArgumentException if the handler does not implement the {@see Tag} interface - * - * @return void - */ - public function registerTagHandler($tagName, $handler); -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}throws tag in a Docblock. - */ -final class Throws extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'throws'; - - /** @var Type */ - private $type; - - public function __construct(Type $type, Description $description = null) - { - $this->type = $type; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::string($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/\s+/Su', $body, 2); - - $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context); - $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context); - - return new static($type, $description); - } - - /** - * Returns the type section of the variable. - * - * @return Type - */ - public function getType() - { - return $this->type; - } - - public function __toString() - { - return $this->type . ' ' . $this->description; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Fqsen; -use phpDocumentor\Reflection\FqsenResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a @covers tag in a Docblock. - */ -final class Covers extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'covers'; - - /** @var Fqsen */ - private $refers = null; - - /** - * Initializes this tag. - * - * @param Fqsen $refers - * @param Description $description - */ - public function __construct(Fqsen $refers, Description $description = null) - { - $this->refers = $refers; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - DescriptionFactory $descriptionFactory = null, - FqsenResolver $resolver = null, - TypeContext $context = null - ) { - Assert::string($body); - Assert::notEmpty($body); - - $parts = preg_split('/\s+/Su', $body, 2); - - return new static( - $resolver->resolve($parts[0], $context), - $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context) - ); - } - - /** - * Returns the structural element this tag refers to. - * - * @return Fqsen - */ - public function getReference() - { - return $this->refers; - } - - /** - * Returns a string representation of this tag. - * - * @return string - */ - public function __toString() - { - return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}source tag in a Docblock. - */ -final class Source extends BaseTag implements Factory\StaticMethod -{ - /** @var string */ - protected $name = 'source'; - - /** @var int The starting line, relative to the structural element's location. */ - private $startingLine = 1; - - /** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */ - private $lineCount = null; - - public function __construct($startingLine, $lineCount = null, Description $description = null) - { - Assert::integerish($startingLine); - Assert::nullOrIntegerish($lineCount); - - $this->startingLine = (int)$startingLine; - $this->lineCount = $lineCount !== null ? (int)$lineCount : null; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) - { - Assert::stringNotEmpty($body); - Assert::notNull($descriptionFactory); - - $startingLine = 1; - $lineCount = null; - $description = null; - - // Starting line / Number of lines / Description - if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) { - $startingLine = (int)$matches[1]; - if (isset($matches[2]) && $matches[2] !== '') { - $lineCount = (int)$matches[2]; - } - - $description = $matches[3]; - } - - return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context)); - } - - /** - * Gets the starting line. - * - * @return int The starting line, relative to the structural element's - * location. - */ - public function getStartingLine() - { - return $this->startingLine; - } - - /** - * Returns the number of lines. - * - * @return int|null The number of lines, relative to the starting line. NULL - * means "to the end". - */ - public function getLineCount() - { - return $this->lineCount; - } - - public function __toString() - { - return $this->startingLine - . ($this->lineCount !== null ? ' ' . $this->lineCount : '') - . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}property-write tag in a Docblock. - */ -class PropertyWrite extends BaseTag implements Factory\StaticMethod -{ - /** @var string */ - protected $name = 'property-write'; - - /** @var Type */ - private $type; - - /** @var string */ - protected $variableName = ''; - - /** - * @param string $variableName - * @param Type $type - * @param Description $description - */ - public function __construct($variableName, Type $type = null, Description $description = null) - { - Assert::string($variableName); - - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::stringNotEmpty($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - - // if the first item that is encountered is not a variable; it is a type - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { - $type = $typeResolver->resolve(array_shift($parts), $context); - array_shift($parts); - } - - // if the next item starts with a $ or ...$ it must be the variable name - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { - $variableName = array_shift($parts); - array_shift($parts); - - if (substr($variableName, 0, 1) === '$') { - $variableName = substr($variableName, 1); - } - } - - $description = $descriptionFactory->create(implode('', $parts), $context); - - return new static($variableName, $type, $description); - } - - /** - * Returns the variable's name. - * - * @return string - */ - public function getVariableName() - { - return $this->variableName; - } - - /** - * Returns the variable's type or null if unknown. - * - * @return Type|null - */ - public function getType() - { - return $this->type; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return ($this->type ? $this->type . ' ' : '') - . '$' . $this->variableName - . ($this->description ? ' ' . $this->description : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}return tag in a Docblock. - */ -final class Return_ extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'return'; - - /** @var Type */ - private $type; - - public function __construct(Type $type, Description $description = null) - { - $this->type = $type; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::string($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/\s+/Su', $body, 2); - - $type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context); - $description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context); - - return new static($type, $description); - } - - /** - * Returns the type section of the variable. - * - * @return Type - */ - public function getType() - { - return $this->type; - } - - public function __toString() - { - return $this->type . ' ' . $this->description; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\DocBlock\StandardTagFactory; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Parses a tag definition for a DocBlock. - */ -class Generic extends BaseTag implements Factory\StaticMethod -{ - /** - * Parses a tag and populates the member variables. - * - * @param string $name Name of the tag. - * @param Description $description The contents of the given tag. - */ - public function __construct($name, Description $description = null) - { - $this->validateTagName($name); - - $this->name = $name; - $this->description = $description; - } - - /** - * Creates a new tag that represents any unknown tag type. - * - * @param string $body - * @param string $name - * @param DescriptionFactory $descriptionFactory - * @param TypeContext $context - * - * @return static - */ - public static function create( - $body, - $name = '', - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::string($body); - Assert::stringNotEmpty($name); - Assert::notNull($descriptionFactory); - - $description = $descriptionFactory && $body ? $descriptionFactory->create($body, $context) : null; - - return new static($name, $description); - } - - /** - * Returns the tag as a serialized string - * - * @return string - */ - public function __toString() - { - return ($this->description ? $this->description->render() : ''); - } - - /** - * Validates if the tag name matches the expected format, otherwise throws an exception. - * - * @param string $name - * - * @return void - */ - private function validateTagName($name) - { - if (! preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) { - throw new \InvalidArgumentException( - 'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, ' - . 'hyphens and backslashes.' - ); - } - } -} - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}version tag in a Docblock. - */ -final class Version extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'version'; - - /** - * PCRE regular expression matching a version vector. - * Assumes the "x" modifier. - */ - const REGEX_VECTOR = '(?: - # Normal release vectors. - \d\S* - | - # VCS version vectors. Per PHPCS, they are expected to - # follow the form of the VCS name, followed by ":", followed - # by the version vector itself. - # By convention, popular VCSes like CVS, SVN and GIT use "$" - # around the actual version vector. - [^\s\:]+\:\s*\$[^\$]+\$ - )'; - - /** @var string The version vector. */ - private $version = ''; - - public function __construct($version = null, Description $description = null) - { - Assert::nullOrStringNotEmpty($version); - - $this->version = $version; - $this->description = $description; - } - - /** - * @return static - */ - public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) - { - Assert::nullOrString($body); - if (empty($body)) { - return new static(); - } - - $matches = []; - if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { - return null; - } - - return new static( - $matches[1], - $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) - ); - } - - /** - * Gets the version section of the tag. - * - * @return string - */ - public function getVersion() - { - return $this->version; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return $this->version . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}deprecated tag in a Docblock. - */ -final class Deprecated extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'deprecated'; - - /** - * PCRE regular expression matching a version vector. - * Assumes the "x" modifier. - */ - const REGEX_VECTOR = '(?: - # Normal release vectors. - \d\S* - | - # VCS version vectors. Per PHPCS, they are expected to - # follow the form of the VCS name, followed by ":", followed - # by the version vector itself. - # By convention, popular VCSes like CVS, SVN and GIT use "$" - # around the actual version vector. - [^\s\:]+\:\s*\$[^\$]+\$ - )'; - - /** @var string The version vector. */ - private $version = ''; - - public function __construct($version = null, Description $description = null) - { - Assert::nullOrStringNotEmpty($version); - - $this->version = $version; - $this->description = $description; - } - - /** - * @return static - */ - public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) - { - Assert::nullOrString($body); - if (empty($body)) { - return new static(); - } - - $matches = []; - if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { - return new static( - null, - null !== $descriptionFactory ? $descriptionFactory->create($body, $context) : null - ); - } - - return new static( - $matches[1], - $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) - ); - } - - /** - * Gets the version section of the tag. - * - * @return string - */ - public function getVersion() - { - return $this->version; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return $this->version . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; - -interface Strategy -{ - public function create($body); -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; - -interface StaticMethod -{ - public static function create($body); -} - - * @copyright 2017 Mike van Riel - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter; - -use phpDocumentor\Reflection\DocBlock\Tag; -use phpDocumentor\Reflection\DocBlock\Tags\Formatter; - -class AlignFormatter implements Formatter -{ - /** @var int The maximum tag name length. */ - protected $maxLen = 0; - - /** - * Constructor. - * - * @param Tag[] $tags All tags that should later be aligned with the formatter. - */ - public function __construct(array $tags) - { - foreach ($tags as $tag) { - $this->maxLen = max($this->maxLen, strlen($tag->getName())); - } - } - - /** - * Formats the given tag to return a simple plain text version. - * - * @param Tag $tag - * - * @return string - */ - public function format(Tag $tag) - { - return '@' . $tag->getName() . str_repeat(' ', $this->maxLen - strlen($tag->getName()) + 1) . (string)$tag; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter; - -use phpDocumentor\Reflection\DocBlock\Tag; -use phpDocumentor\Reflection\DocBlock\Tags\Formatter; - -class PassthroughFormatter implements Formatter -{ - /** - * Formats the given tag to return a simple plain text version. - * - * @param Tag $tag - * - * @return string - */ - public function format(Tag $tag) - { - return trim('@' . $tag->getName() . ' ' . (string)$tag); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock; -use phpDocumentor\Reflection\DocBlock\Description; - -/** - * Parses a tag definition for a DocBlock. - */ -abstract class BaseTag implements DocBlock\Tag -{ - /** @var string Name of the tag */ - protected $name = ''; - - /** @var Description|null Description of the tag. */ - protected $description; - - /** - * Gets the name of this tag. - * - * @return string The name of this tag. - */ - public function getName() - { - return $this->name; - } - - public function getDescription() - { - return $this->description; - } - - public function render(Formatter $formatter = null) - { - if ($formatter === null) { - $formatter = new Formatter\PassthroughFormatter(); - } - - return $formatter->format($this); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\Tag; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}example tag in a Docblock. - */ -final class Example extends BaseTag -{ - /** - * @var string Path to a file to use as an example. May also be an absolute URI. - */ - private $filePath; - - /** - * @var bool Whether the file path component represents an URI. This determines how the file portion - * appears at {@link getContent()}. - */ - private $isURI = false; - - /** - * @var int - */ - private $startingLine; - - /** - * @var int - */ - private $lineCount; - - public function __construct($filePath, $isURI, $startingLine, $lineCount, $description) - { - Assert::notEmpty($filePath); - Assert::integer($startingLine); - Assert::greaterThanEq($startingLine, 0); - - $this->filePath = $filePath; - $this->startingLine = $startingLine; - $this->lineCount = $lineCount; - $this->name = 'example'; - if ($description !== null) { - $this->description = trim($description); - } - - $this->isURI = $isURI; - } - - /** - * {@inheritdoc} - */ - public function getContent() - { - if (null === $this->description) { - $filePath = '"' . $this->filePath . '"'; - if ($this->isURI) { - $filePath = $this->isUriRelative($this->filePath) - ? str_replace('%2F', '/', rawurlencode($this->filePath)) - :$this->filePath; - } - - return trim($filePath . ' ' . parent::getDescription()); - } - - return $this->description; - } - - /** - * {@inheritdoc} - */ - public static function create($body) - { - // File component: File path in quotes or File URI / Source information - if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) { - return null; - } - - $filePath = null; - $fileUri = null; - if ('' !== $matches[1]) { - $filePath = $matches[1]; - } else { - $fileUri = $matches[2]; - } - - $startingLine = 1; - $lineCount = null; - $description = null; - - if (array_key_exists(3, $matches)) { - $description = $matches[3]; - - // Starting line / Number of lines / Description - if (preg_match('/^([1-9]\d*)(?:\s+((?1))\s*)?(.*)$/sux', $matches[3], $contentMatches)) { - $startingLine = (int)$contentMatches[1]; - if (isset($contentMatches[2]) && $contentMatches[2] !== '') { - $lineCount = (int)$contentMatches[2]; - } - - if (array_key_exists(3, $contentMatches)) { - $description = $contentMatches[3]; - } - } - } - - return new static( - $filePath !== null?$filePath:$fileUri, - $fileUri !== null, - $startingLine, - $lineCount, - $description - ); - } - - /** - * Returns the file path. - * - * @return string Path to a file to use as an example. - * May also be an absolute URI. - */ - public function getFilePath() - { - return $this->filePath; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return $this->filePath . ($this->description ? ' ' . $this->description : ''); - } - - /** - * Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute). - * - * @param string $uri - * - * @return bool - */ - private function isUriRelative($uri) - { - return false === strpos($uri, ':'); - } - - /** - * @return int - */ - public function getStartingLine() - { - return $this->startingLine; - } - - /** - * @return int - */ - public function getLineCount() - { - return $this->lineCount; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}property tag in a Docblock. - */ -class Property extends BaseTag implements Factory\StaticMethod -{ - /** @var string */ - protected $name = 'property'; - - /** @var Type */ - private $type; - - /** @var string */ - protected $variableName = ''; - - /** - * @param string $variableName - * @param Type $type - * @param Description $description - */ - public function __construct($variableName, Type $type = null, Description $description = null) - { - Assert::string($variableName); - - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::stringNotEmpty($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - - // if the first item that is encountered is not a variable; it is a type - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { - $type = $typeResolver->resolve(array_shift($parts), $context); - array_shift($parts); - } - - // if the next item starts with a $ or ...$ it must be the variable name - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { - $variableName = array_shift($parts); - array_shift($parts); - - if (substr($variableName, 0, 1) === '$') { - $variableName = substr($variableName, 1); - } - } - - $description = $descriptionFactory->create(implode('', $parts), $context); - - return new static($variableName, $type, $description); - } - - /** - * Returns the variable's name. - * - * @return string - */ - public function getVariableName() - { - return $this->variableName; - } - - /** - * Returns the variable's type or null if unknown. - * - * @return Type|null - */ - public function getType() - { - return $this->type; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return ($this->type ? $this->type . ' ' : '') - . '$' . $this->variableName - . ($this->description ? ' ' . $this->description : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef; -use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference; -use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url; -use phpDocumentor\Reflection\FqsenResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for an {@}see tag in a Docblock. - */ -class See extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'see'; - - /** @var Reference */ - protected $refers = null; - - /** - * Initializes this tag. - * - * @param Reference $refers - * @param Description $description - */ - public function __construct(Reference $refers, Description $description = null) - { - $this->refers = $refers; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - FqsenResolver $resolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::string($body); - Assert::allNotNull([$resolver, $descriptionFactory]); - - $parts = preg_split('/\s+/Su', $body, 2); - $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; - - // https://tools.ietf.org/html/rfc2396#section-3 - if (preg_match('/\w:\/\/\w/i', $parts[0])) { - return new static(new Url($parts[0]), $description); - } - - return new static(new FqsenRef($resolver->resolve($parts[0], $context)), $description); - } - - /** - * Returns the ref of this tag. - * - * @return Reference - */ - public function getReference() - { - return $this->refers; - } - - /** - * Returns a string representation of this tag. - * - * @return string - */ - public function __toString() - { - return $this->refers . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}property-read tag in a Docblock. - */ -class PropertyRead extends BaseTag implements Factory\StaticMethod -{ - /** @var string */ - protected $name = 'property-read'; - - /** @var Type */ - private $type; - - /** @var string */ - protected $variableName = ''; - - /** - * @param string $variableName - * @param Type $type - * @param Description $description - */ - public function __construct($variableName, Type $type = null, Description $description = null) - { - Assert::string($variableName); - - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::stringNotEmpty($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - - // if the first item that is encountered is not a variable; it is a type - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { - $type = $typeResolver->resolve(array_shift($parts), $context); - array_shift($parts); - } - - // if the next item starts with a $ or ...$ it must be the variable name - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { - $variableName = array_shift($parts); - array_shift($parts); - - if (substr($variableName, 0, 1) === '$') { - $variableName = substr($variableName, 1); - } - } - - $description = $descriptionFactory->create(implode('', $parts), $context); - - return new static($variableName, $type, $description); - } - - /** - * Returns the variable's name. - * - * @return string - */ - public function getVariableName() - { - return $this->variableName; - } - - /** - * Returns the variable's type or null if unknown. - * - * @return Type|null - */ - public function getType() - { - return $this->type; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return ($this->type ? $this->type . ' ' : '') - . '$' . $this->variableName - . ($this->description ? ' ' . $this->description : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use Webmozart\Assert\Assert; - -/** - * Reflection class for an {@}author tag in a Docblock. - */ -final class Author extends BaseTag implements Factory\StaticMethod -{ - /** @var string register that this is the author tag. */ - protected $name = 'author'; - - /** @var string The name of the author */ - private $authorName = ''; - - /** @var string The email of the author */ - private $authorEmail = ''; - - /** - * Initializes this tag with the author name and e-mail. - * - * @param string $authorName - * @param string $authorEmail - */ - public function __construct($authorName, $authorEmail) - { - Assert::string($authorName); - Assert::string($authorEmail); - if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) { - throw new \InvalidArgumentException('The author tag does not have a valid e-mail address'); - } - - $this->authorName = $authorName; - $this->authorEmail = $authorEmail; - } - - /** - * Gets the author's name. - * - * @return string The author's name. - */ - public function getAuthorName() - { - return $this->authorName; - } - - /** - * Returns the author's email. - * - * @return string The author's email. - */ - public function getEmail() - { - return $this->authorEmail; - } - - /** - * Returns this tag in string form. - * - * @return string - */ - public function __toString() - { - return $this->authorName . (strlen($this->authorEmail) ? ' <' . $this->authorEmail . '>' : ''); - } - - /** - * Attempts to create a new Author object based on †he tag body. - * - * @param string $body - * - * @return static - */ - public static function create($body) - { - Assert::string($body); - - $splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches); - if (!$splitTagContent) { - return null; - } - - $authorName = trim($matches[1]); - $email = isset($matches[2]) ? trim($matches[2]) : ''; - - return new static($authorName, $email); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}since tag in a Docblock. - */ -final class Since extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'since'; - - /** - * PCRE regular expression matching a version vector. - * Assumes the "x" modifier. - */ - const REGEX_VECTOR = '(?: - # Normal release vectors. - \d\S* - | - # VCS version vectors. Per PHPCS, they are expected to - # follow the form of the VCS name, followed by ":", followed - # by the version vector itself. - # By convention, popular VCSes like CVS, SVN and GIT use "$" - # around the actual version vector. - [^\s\:]+\:\s*\$[^\$]+\$ - )'; - - /** @var string The version vector. */ - private $version = ''; - - public function __construct($version = null, Description $description = null) - { - Assert::nullOrStringNotEmpty($version); - - $this->version = $version; - $this->description = $description; - } - - /** - * @return static - */ - public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) - { - Assert::nullOrString($body); - if (empty($body)) { - return new static(); - } - - $matches = []; - if (! preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) { - return null; - } - - return new static( - $matches[1], - $descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context) - ); - } - - /** - * Gets the version section of the tag. - * - * @return string - */ - public function getVersion() - { - return $this->version; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return $this->version . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com) - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a @link tag in a Docblock. - */ -final class Link extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'link'; - - /** @var string */ - private $link = ''; - - /** - * Initializes a link to a URL. - * - * @param string $link - * @param Description $description - */ - public function __construct($link, Description $description = null) - { - Assert::string($link); - - $this->link = $link; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null) - { - Assert::string($body); - Assert::notNull($descriptionFactory); - - $parts = preg_split('/\s+/Su', $body, 2); - $description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null; - - return new static($parts[0], $description); - } - - /** - * Gets the link - * - * @return string - */ - public function getLink() - { - return $this->link; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return $this->link . ($this->description ? ' ' . $this->description->render() : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use phpDocumentor\Reflection\Types\Void_; -use Webmozart\Assert\Assert; - -/** - * Reflection class for an {@}method in a Docblock. - */ -final class Method extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'method'; - - /** @var string */ - private $methodName = ''; - - /** @var string[] */ - private $arguments = []; - - /** @var bool */ - private $isStatic = false; - - /** @var Type */ - private $returnType; - - public function __construct( - $methodName, - array $arguments = [], - Type $returnType = null, - $static = false, - Description $description = null - ) { - Assert::stringNotEmpty($methodName); - Assert::boolean($static); - - if ($returnType === null) { - $returnType = new Void_(); - } - - $this->methodName = $methodName; - $this->arguments = $this->filterArguments($arguments); - $this->returnType = $returnType; - $this->isStatic = $static; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::stringNotEmpty($body); - Assert::allNotNull([ $typeResolver, $descriptionFactory ]); - - // 1. none or more whitespace - // 2. optionally the keyword "static" followed by whitespace - // 3. optionally a word with underscores followed by whitespace : as - // type for the return value - // 4. then optionally a word with underscores followed by () and - // whitespace : as method name as used by phpDocumentor - // 5. then a word with underscores, followed by ( and any character - // until a ) and whitespace : as method name with signature - // 6. any remaining text : as description - if (!preg_match( - '/^ - # Static keyword - # Declares a static method ONLY if type is also present - (?: - (static) - \s+ - )? - # Return type - (?: - ( - (?:[\w\|_\\\\]*\$this[\w\|_\\\\]*) - | - (?: - (?:[\w\|_\\\\]+) - # array notation - (?:\[\])* - )* - ) - \s+ - )? - # Legacy method name (not captured) - (?: - [\w_]+\(\)\s+ - )? - # Method name - ([\w\|_\\\\]+) - # Arguments - (?: - \(([^\)]*)\) - )? - \s* - # Description - (.*) - $/sux', - $body, - $matches - )) { - return null; - } - - list(, $static, $returnType, $methodName, $arguments, $description) = $matches; - - $static = $static === 'static'; - - if ($returnType === '') { - $returnType = 'void'; - } - - $returnType = $typeResolver->resolve($returnType, $context); - $description = $descriptionFactory->create($description, $context); - - if (is_string($arguments) && strlen($arguments) > 0) { - $arguments = explode(',', $arguments); - foreach ($arguments as &$argument) { - $argument = explode(' ', self::stripRestArg(trim($argument)), 2); - if ($argument[0][0] === '$') { - $argumentName = substr($argument[0], 1); - $argumentType = new Void_(); - } else { - $argumentType = $typeResolver->resolve($argument[0], $context); - $argumentName = ''; - if (isset($argument[1])) { - $argument[1] = self::stripRestArg($argument[1]); - $argumentName = substr($argument[1], 1); - } - } - - $argument = [ 'name' => $argumentName, 'type' => $argumentType]; - } - } else { - $arguments = []; - } - - return new static($methodName, $arguments, $returnType, $static, $description); - } - - /** - * Retrieves the method name. - * - * @return string - */ - public function getMethodName() - { - return $this->methodName; - } - - /** - * @return string[] - */ - public function getArguments() - { - return $this->arguments; - } - - /** - * Checks whether the method tag describes a static method or not. - * - * @return bool TRUE if the method declaration is for a static method, FALSE otherwise. - */ - public function isStatic() - { - return $this->isStatic; - } - - /** - * @return Type - */ - public function getReturnType() - { - return $this->returnType; - } - - public function __toString() - { - $arguments = []; - foreach ($this->arguments as $argument) { - $arguments[] = $argument['type'] . ' $' . $argument['name']; - } - - return trim(($this->isStatic() ? 'static ' : '') - . (string)$this->returnType . ' ' - . $this->methodName - . '(' . implode(', ', $arguments) . ')' - . ($this->description ? ' ' . $this->description->render() : '')); - } - - private function filterArguments($arguments) - { - foreach ($arguments as &$argument) { - if (is_string($argument)) { - $argument = [ 'name' => $argument ]; - } - - if (! isset($argument['type'])) { - $argument['type'] = new Void_(); - } - - $keys = array_keys($argument); - sort($keys); - if ($keys !== [ 'name', 'type' ]) { - throw new \InvalidArgumentException( - 'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true) - ); - } - } - - return $arguments; - } - - private static function stripRestArg($argument) - { - if (strpos($argument, '...') === 0) { - $argument = trim(substr($argument, 3)); - } - - return $argument; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Tag; - -interface Formatter -{ - /** - * Formats a tag into a string representation according to a specific format, such as Markdown. - * - * @param Tag $tag - * - * @return string - */ - public function format(Tag $tag); -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for the {@}param tag in a Docblock. - */ -final class Param extends BaseTag implements Factory\StaticMethod -{ - /** @var string */ - protected $name = 'param'; - - /** @var Type */ - private $type; - - /** @var string */ - private $variableName = ''; - - /** @var bool determines whether this is a variadic argument */ - private $isVariadic = false; - - /** - * @param string $variableName - * @param Type $type - * @param bool $isVariadic - * @param Description $description - */ - public function __construct($variableName, Type $type = null, $isVariadic = false, Description $description = null) - { - Assert::string($variableName); - Assert::boolean($isVariadic); - - $this->variableName = $variableName; - $this->type = $type; - $this->isVariadic = $isVariadic; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::stringNotEmpty($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - $isVariadic = false; - - // if the first item that is encountered is not a variable; it is a type - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { - $type = $typeResolver->resolve(array_shift($parts), $context); - array_shift($parts); - } - - // if the next item starts with a $ or ...$ it must be the variable name - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$' || substr($parts[0], 0, 4) === '...$')) { - $variableName = array_shift($parts); - array_shift($parts); - - if (substr($variableName, 0, 3) === '...') { - $isVariadic = true; - $variableName = substr($variableName, 3); - } - - if (substr($variableName, 0, 1) === '$') { - $variableName = substr($variableName, 1); - } - } - - $description = $descriptionFactory->create(implode('', $parts), $context); - - return new static($variableName, $type, $isVariadic, $description); - } - - /** - * Returns the variable's name. - * - * @return string - */ - public function getVariableName() - { - return $this->variableName; - } - - /** - * Returns the variable's type or null if unknown. - * - * @return Type|null - */ - public function getType() - { - return $this->type; - } - - /** - * Returns whether this tag is variadic. - * - * @return boolean - */ - public function isVariadic() - { - return $this->isVariadic; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return ($this->type ? $this->type . ' ' : '') - . ($this->isVariadic() ? '...' : '') - . '$' . $this->variableName - . ($this->description ? ' ' . $this->description : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Type; -use phpDocumentor\Reflection\TypeResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}var tag in a Docblock. - */ -class Var_ extends BaseTag implements Factory\StaticMethod -{ - /** @var string */ - protected $name = 'var'; - - /** @var Type */ - private $type; - - /** @var string */ - protected $variableName = ''; - - /** - * @param string $variableName - * @param Type $type - * @param Description $description - */ - public function __construct($variableName, Type $type = null, Description $description = null) - { - Assert::string($variableName); - - $this->variableName = $variableName; - $this->type = $type; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - TypeResolver $typeResolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::stringNotEmpty($body); - Assert::allNotNull([$typeResolver, $descriptionFactory]); - - $parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE); - $type = null; - $variableName = ''; - - // if the first item that is encountered is not a variable; it is a type - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) { - $type = $typeResolver->resolve(array_shift($parts), $context); - array_shift($parts); - } - - // if the next item starts with a $ or ...$ it must be the variable name - if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) { - $variableName = array_shift($parts); - array_shift($parts); - - if (substr($variableName, 0, 1) === '$') { - $variableName = substr($variableName, 1); - } - } - - $description = $descriptionFactory->create(implode('', $parts), $context); - - return new static($variableName, $type, $description); - } - - /** - * Returns the variable's name. - * - * @return string - */ - public function getVariableName() - { - return $this->variableName; - } - - /** - * Returns the variable's type or null if unknown. - * - * @return Type|null - */ - public function getType() - { - return $this->type; - } - - /** - * Returns a string representation for this tag. - * - * @return string - */ - public function __toString() - { - return ($this->type ? $this->type . ' ' : '') - . (empty($this->variableName) ? null : ('$' . $this->variableName)) - . ($this->description ? ' ' . $this->description : ''); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; - -use Webmozart\Assert\Assert; - -/** - * Url reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See} - */ -final class Url implements Reference -{ - /** - * @var string - */ - private $uri; - - /** - * Url constructor. - */ - public function __construct($uri) - { - Assert::stringNotEmpty($uri); - $this->uri = $uri; - } - - public function __toString() - { - return $this->uri; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; - -use phpDocumentor\Reflection\Fqsen as RealFqsen; - -/** - * Fqsen reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See} - */ -final class Fqsen implements Reference -{ - /** - * @var RealFqsen - */ - private $fqsen; - - /** - * Fqsen constructor. - */ - public function __construct(RealFqsen $fqsen) - { - $this->fqsen = $fqsen; - } - - /** - * @return string string representation of the referenced fqsen - */ - public function __toString() - { - return (string)$this->fqsen; - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags\Reference; - -/** - * Interface for references in {@see phpDocumentor\Reflection\DocBlock\Tags\See} - */ -interface Reference -{ - public function __toString(); -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock\Tags; - -use phpDocumentor\Reflection\DocBlock\Description; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\Fqsen; -use phpDocumentor\Reflection\FqsenResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Reflection class for a {@}uses tag in a Docblock. - */ -final class Uses extends BaseTag implements Factory\StaticMethod -{ - protected $name = 'uses'; - - /** @var Fqsen */ - protected $refers = null; - - /** - * Initializes this tag. - * - * @param Fqsen $refers - * @param Description $description - */ - public function __construct(Fqsen $refers, Description $description = null) - { - $this->refers = $refers; - $this->description = $description; - } - - /** - * {@inheritdoc} - */ - public static function create( - $body, - FqsenResolver $resolver = null, - DescriptionFactory $descriptionFactory = null, - TypeContext $context = null - ) { - Assert::string($body); - Assert::allNotNull([$resolver, $descriptionFactory]); - - $parts = preg_split('/\s+/Su', $body, 2); - - return new static( - $resolver->resolve($parts[0], $context), - $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context) - ); - } - - /** - * Returns the structural element this tag refers to. - * - * @return Fqsen - */ - public function getReference() - { - return $this->refers; - } - - /** - * Returns a string representation of this tag. - * - * @return string - */ - public function __toString() - { - return $this->refers . ' ' . $this->description->render(); - } -} - - * @license http://www.opensource.org/licenses/mit-license.php MIT - * @link http://phpdoc.org - */ - -namespace phpDocumentor\Reflection\DocBlock; - -use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod; -use phpDocumentor\Reflection\DocBlock\Tags\Generic; -use phpDocumentor\Reflection\FqsenResolver; -use phpDocumentor\Reflection\Types\Context as TypeContext; -use Webmozart\Assert\Assert; - -/** - * Creates a Tag object given the contents of a tag. - * - * This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create` - * factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can - * pass the dependencies that you need to construct a tag object. - * - * > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise - * > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to - * > verify that a dependency is actually passed. - * - * This Factory also features a Service Locator component that is used to pass the right dependencies to the - * `create` method of a tag; each dependency should be registered as a service or as a parameter. - * - * When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass - * the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface. - */ -final class StandardTagFactory implements TagFactory -{ - /** PCRE regular expression matching a tag name. */ - const REGEX_TAGNAME = '[\w\-\_\\\\]+'; - - /** - * @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value. - */ - private $tagHandlerMappings = [ - 'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author', - 'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers', - 'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated', - // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', - 'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link', - 'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method', - 'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param', - 'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead', - 'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property', - 'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite', - 'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return_', - 'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See', - 'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since', - 'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source', - 'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', - 'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws', - 'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses', - 'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_', - 'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version' - ]; - - /** - * @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used. - */ - private $tagHandlerParameterCache = []; - - /** - * @var FqsenResolver - */ - private $fqsenResolver; - - /** - * @var mixed[] an array representing a simple Service Locator where we can store parameters and - * services that can be inserted into the Factory Methods of Tag Handlers. - */ - private $serviceLocator = []; - - /** - * Initialize this tag factory with the means to resolve an FQSEN and optionally a list of tag handlers. - * - * If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property - * is used. - * - * @param FqsenResolver $fqsenResolver - * @param string[] $tagHandlers - * - * @see self::registerTagHandler() to add a new tag handler to the existing default list. - */ - public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null) - { - $this->fqsenResolver = $fqsenResolver; - if ($tagHandlers !== null) { - $this->tagHandlerMappings = $tagHandlers; - } - - $this->addService($fqsenResolver, FqsenResolver::class); - } - - /** - * {@inheritDoc} - */ - public function create($tagLine, TypeContext $context = null) - { - if (! $context) { - $context = new TypeContext(''); - } - - list($tagName, $tagBody) = $this->extractTagParts($tagLine); - - if ($tagBody !== '' && $tagBody[0] === '[') { - throw new \InvalidArgumentException( - 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' - ); - } - - return $this->createTag($tagBody, $tagName, $context); - } - - /** - * {@inheritDoc} - */ - public function addParameter($name, $value) - { - $this->serviceLocator[$name] = $value; - } - - /** - * {@inheritDoc} - */ - public function addService($service, $alias = null) - { - $this->serviceLocator[$alias ?: get_class($service)] = $service; - } - - /** - * {@inheritDoc} - */ - public function registerTagHandler($tagName, $handler) - { - Assert::stringNotEmpty($tagName); - Assert::stringNotEmpty($handler); - Assert::classExists($handler); - Assert::implementsInterface($handler, StaticMethod::class); - - if (strpos($tagName, '\\') && $tagName[0] !== '\\') { - throw new \InvalidArgumentException( - 'A namespaced tag must have a leading backslash as it must be fully qualified' - ); - } - - $this->tagHandlerMappings[$tagName] = $handler; - } - - /** - * Extracts all components for a tag. - * - * @param string $tagLine - * - * @return string[] - */ - private function extractTagParts($tagLine) - { - $matches = []; - if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)/us', $tagLine, $matches)) { - throw new \InvalidArgumentException( - 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' - ); - } - - if (count($matches) < 3) { - $matches[] = ''; - } - - return array_slice($matches, 1); - } - - /** - * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the - * body was invalid. - * - * @param string $body - * @param string $name - * @param TypeContext $context - * - * @return Tag|null - */ - private function createTag($body, $name, TypeContext $context) - { - $handlerClassName = $this->findHandlerClassName($name, $context); - $arguments = $this->getArgumentsForParametersFromWiring( - $this->fetchParametersForHandlerFactoryMethod($handlerClassName), - $this->getServiceLocatorWithDynamicParameters($context, $name, $body) - ); - - return call_user_func_array([$handlerClassName, 'create'], $arguments); - } - - /** - * Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`). - * - * @param string $tagName - * @param TypeContext $context - * - * @return string - */ - private function findHandlerClassName($tagName, TypeContext $context) - { - $handlerClassName = Generic::class; - if (isset($this->tagHandlerMappings[$tagName])) { - $handlerClassName = $this->tagHandlerMappings[$tagName]; - } elseif ($this->isAnnotation($tagName)) { - // TODO: Annotation support is planned for a later stage and as such is disabled for now - // $tagName = (string)$this->fqsenResolver->resolve($tagName, $context); - // if (isset($this->annotationMappings[$tagName])) { - // $handlerClassName = $this->annotationMappings[$tagName]; - // } - } - - return $handlerClassName; - } - - /** - * Retrieves the arguments that need to be passed to the Factory Method with the given Parameters. - * - * @param \ReflectionParameter[] $parameters - * @param mixed[] $locator - * - * @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters - * is provided with this method. - */ - private function getArgumentsForParametersFromWiring($parameters, $locator) - { - $arguments = []; - foreach ($parameters as $index => $parameter) { - $typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null; - if (isset($locator[$typeHint])) { - $arguments[] = $locator[$typeHint]; - continue; - } - - $parameterName = $parameter->getName(); - if (isset($locator[$parameterName])) { - $arguments[] = $locator[$parameterName]; - continue; - } - - $arguments[] = null; - } - - return $arguments; - } - - /** - * Retrieves a series of ReflectionParameter objects for the static 'create' method of the given - * tag handler class name. - * - * @param string $handlerClassName - * - * @return \ReflectionParameter[] - */ - private function fetchParametersForHandlerFactoryMethod($handlerClassName) - { - if (! isset($this->tagHandlerParameterCache[$handlerClassName])) { - $methodReflection = new \ReflectionMethod($handlerClassName, 'create'); - $this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters(); - } - - return $this->tagHandlerParameterCache[$handlerClassName]; - } - - /** - * Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and - * Context. - * - * @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs. - * @param string $tagName The name of the tag that may be passed onto the factory method of the Tag class. - * @param string $tagBody The body of the tag that may be passed onto the factory method of the Tag class. - * - * @return mixed[] - */ - private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody) - { - $locator = array_merge( - $this->serviceLocator, - [ - 'name' => $tagName, - 'body' => $tagBody, - TypeContext::class => $context - ] - ); - - return $locator; - } - - /** - * Returns whether the given tag belongs to an annotation. - * - * @param string $tagContent - * - * @todo this method should be populated once we implement Annotation notation support. - * - * @return bool - */ - private function isAnnotation($tagContent) - { - // 1. Contains a namespace separator - // 2. Contains parenthesis - // 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part - // of the annotation class name matches the found tag name - - return false; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ComponentElementCollection extends ElementCollection { - public function current() { - return new ComponentElement( - $this->getCurrentElement() - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use LibXMLError; - -class ManifestDocumentLoadingException extends \Exception implements Exception { - /** - * @var LibXMLError[] - */ - private $libxmlErrors; - - /** - * ManifestDocumentLoadingException constructor. - * - * @param LibXMLError[] $libxmlErrors - */ - public function __construct(array $libxmlErrors) { - $this->libxmlErrors = $libxmlErrors; - $first = $this->libxmlErrors[0]; - - parent::__construct( - sprintf( - '%s (Line: %d / Column: %d / File: %s)', - $first->message, - $first->line, - $first->column, - $first->file - ), - $first->code - ); - } - - /** - * @return LibXMLError[] - */ - public function getLibxmlErrors() { - return $this->libxmlErrors; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class LicenseElement extends ManifestElement { - public function getType() { - return $this->getAttributeValue('type'); - } - - public function getUrl() { - return $this->getAttributeValue('url'); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class RequiresElement extends ManifestElement { - public function getPHPElement() { - return new PhpElement( - $this->getChildByName('php') - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class CopyrightElement extends ManifestElement { - public function getAuthorElements() { - return new AuthorElementCollection( - $this->getChildrenByName('author') - ); - } - - public function getLicenseElement() { - return new LicenseElement( - $this->getChildByName('license') - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ExtElementCollection extends ElementCollection { - public function current() { - return new ExtElement( - $this->getCurrentElement() - ); - } - -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ExtensionElement extends ManifestElement { - public function getFor() { - return $this->getAttributeValue('for'); - } - - public function getCompatible() { - return $this->getAttributeValue('compatible'); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class AuthorElement extends ManifestElement { - public function getName() { - return $this->getAttributeValue('name'); - } - - public function getEmail() { - return $this->getAttributeValue('email'); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class BundlesElement extends ManifestElement { - public function getComponentElements() { - return new ComponentElementCollection( - $this->getChildrenByName('component') - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ComponentElement extends ManifestElement { - public function getName() { - return $this->getAttributeValue('name'); - } - - public function getVersion() { - return $this->getAttributeValue('version'); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use DOMElement; -use DOMNodeList; - -abstract class ElementCollection implements \Iterator { - /** - * @var DOMNodeList - */ - private $nodeList; - - private $position; - - /** - * ElementCollection constructor. - * - * @param DOMNodeList $nodeList - */ - public function __construct(DOMNodeList $nodeList) { - $this->nodeList = $nodeList; - $this->position = 0; - } - - abstract public function current(); - - /** - * @return DOMElement - */ - protected function getCurrentElement() { - return $this->nodeList->item($this->position); - } - - public function next() { - $this->position++; - } - - public function key() { - return $this->position; - } - - public function valid() { - return $this->position < $this->nodeList->length; - } - - public function rewind() { - $this->position = 0; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class AuthorElementCollection extends ElementCollection { - public function current() { - return new AuthorElement( - $this->getCurrentElement() - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class PhpElement extends ManifestElement { - public function getVersion() { - return $this->getAttributeValue('version'); - } - - public function hasExtElements() { - return $this->hasChild('ext'); - } - - public function getExtElements() { - return new ExtElementCollection( - $this->getChildrenByName('ext') - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ExtElement extends ManifestElement { - public function getName() { - return $this->getAttributeValue('name'); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use DOMElement; -use DOMNodeList; - -class ManifestElement { - const XMLNS = 'https://phar.io/xml/manifest/1.0'; - - /** - * @var DOMElement - */ - private $element; - - /** - * ContainsElement constructor. - * - * @param DOMElement $element - */ - public function __construct(DOMElement $element) { - $this->element = $element; - } - - /** - * @param string $name - * - * @return string - * - * @throws ManifestElementException - */ - protected function getAttributeValue($name) { - if (!$this->element->hasAttribute($name)) { - throw new ManifestElementException( - sprintf( - 'Attribute %s not set on element %s', - $name, - $this->element->localName - ) - ); - } - - return $this->element->getAttribute($name); - } - - /** - * @param $elementName - * - * @return DOMElement - * - * @throws ManifestElementException - */ - protected function getChildByName($elementName) { - $element = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); - - if (!$element instanceof DOMElement) { - throw new ManifestElementException( - sprintf('Element %s missing', $elementName) - ); - } - - return $element; - } - - /** - * @param $elementName - * - * @return DOMNodeList - * - * @throws ManifestElementException - */ - protected function getChildrenByName($elementName) { - $elementList = $this->element->getElementsByTagNameNS(self::XMLNS, $elementName); - - if ($elementList->length === 0) { - throw new ManifestElementException( - sprintf('Element(s) %s missing', $elementName) - ); - } - - return $elementList; - } - - /** - * @param string $elementName - * - * @return bool - */ - protected function hasChild($elementName) { - return $this->element->getElementsByTagNameNS(self::XMLNS, $elementName)->length !== 0; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use DOMDocument; -use DOMElement; - -class ManifestDocument { - const XMLNS = 'https://phar.io/xml/manifest/1.0'; - - /** - * @var DOMDocument - */ - private $dom; - - /** - * ManifestDocument constructor. - * - * @param DOMDocument $dom - */ - private function __construct(DOMDocument $dom) { - $this->ensureCorrectDocumentType($dom); - - $this->dom = $dom; - } - - public static function fromFile($filename) { - if (!file_exists($filename)) { - throw new ManifestDocumentException( - sprintf('File "%s" not found', $filename) - ); - } - - return self::fromString( - file_get_contents($filename) - ); - } - - public static function fromString($xmlString) { - $prev = libxml_use_internal_errors(true); - libxml_clear_errors(); - - $dom = new DOMDocument(); - $dom->loadXML($xmlString); - - $errors = libxml_get_errors(); - libxml_use_internal_errors($prev); - - if (count($errors) !== 0) { - throw new ManifestDocumentLoadingException($errors); - } - - return new self($dom); - } - - public function getContainsElement() { - return new ContainsElement( - $this->fetchElementByName('contains') - ); - } - - public function getCopyrightElement() { - return new CopyrightElement( - $this->fetchElementByName('copyright') - ); - } - - public function getRequiresElement() { - return new RequiresElement( - $this->fetchElementByName('requires') - ); - } - - public function hasBundlesElement() { - return $this->dom->getElementsByTagNameNS(self::XMLNS, 'bundles')->length === 1; - } - - public function getBundlesElement() { - return new BundlesElement( - $this->fetchElementByName('bundles') - ); - } - - private function ensureCorrectDocumentType(DOMDocument $dom) { - $root = $dom->documentElement; - - if ($root->localName !== 'phar' || $root->namespaceURI !== self::XMLNS) { - throw new ManifestDocumentException('Not a phar.io manifest document'); - } - } - - /** - * @param $elementName - * - * @return DOMElement - * - * @throws ManifestDocumentException - */ - private function fetchElementByName($elementName) { - $element = $this->dom->getElementsByTagNameNS(self::XMLNS, $elementName)->item(0); - - if (!$element instanceof DOMElement) { - throw new ManifestDocumentException( - sprintf('Element %s missing', $elementName) - ); - } - - return $element; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ContainsElement extends ManifestElement { - public function getName() { - return $this->getAttributeValue('name'); - } - - public function getVersion() { - return $this->getAttributeValue('version'); - } - - public function getType() { - return $this->getAttributeValue('type'); - } - - public function getExtensionElement() { - return new ExtensionElement( - $this->getChildByName('extension') - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\AnyVersionConstraint; -use PharIo\Version\Version; -use PharIo\Version\VersionConstraint; -use XMLWriter; - -class ManifestSerializer { - /** - * @var XMLWriter - */ - private $xmlWriter; - - public function serializeToFile(Manifest $manifest, $filename) { - file_put_contents( - $filename, - $this->serializeToString($manifest) - ); - } - - public function serializeToString(Manifest $manifest) { - $this->startDocument(); - - $this->addContains($manifest->getName(), $manifest->getVersion(), $manifest->getType()); - $this->addCopyright($manifest->getCopyrightInformation()); - $this->addRequirements($manifest->getRequirements()); - $this->addBundles($manifest->getBundledComponents()); - - return $this->finishDocument(); - } - - private function startDocument() { - $xmlWriter = new XMLWriter(); - $xmlWriter->openMemory(); - $xmlWriter->setIndent(true); - $xmlWriter->setIndentString(str_repeat(' ', 4)); - $xmlWriter->startDocument('1.0', 'UTF-8'); - $xmlWriter->startElement('phar'); - $xmlWriter->writeAttribute('xmlns', 'https://phar.io/xml/manifest/1.0'); - - $this->xmlWriter = $xmlWriter; - } - - private function finishDocument() { - $this->xmlWriter->endElement(); - $this->xmlWriter->endDocument(); - - return $this->xmlWriter->outputMemory(); - } - - private function addContains($name, Version $version, Type $type) { - $this->xmlWriter->startElement('contains'); - $this->xmlWriter->writeAttribute('name', $name); - $this->xmlWriter->writeAttribute('version', $version->getVersionString()); - - switch (true) { - case $type->isApplication(): { - $this->xmlWriter->writeAttribute('type', 'application'); - break; - } - - case $type->isLibrary(): { - $this->xmlWriter->writeAttribute('type', 'library'); - break; - } - - case $type->isExtension(): { - /* @var $type Extension */ - $this->xmlWriter->writeAttribute('type', 'extension'); - $this->addExtension($type->getApplicationName(), $type->getVersionConstraint()); - break; - } - - default: { - $this->xmlWriter->writeAttribute('type', 'custom'); - } - } - - $this->xmlWriter->endElement(); - } - - private function addCopyright(CopyrightInformation $copyrightInformation) { - $this->xmlWriter->startElement('copyright'); - - foreach($copyrightInformation->getAuthors() as $author) { - $this->xmlWriter->startElement('author'); - $this->xmlWriter->writeAttribute('name', $author->getName()); - $this->xmlWriter->writeAttribute('email', (string) $author->getEmail()); - $this->xmlWriter->endElement(); - } - - $license = $copyrightInformation->getLicense(); - - $this->xmlWriter->startElement('license'); - $this->xmlWriter->writeAttribute('type', $license->getName()); - $this->xmlWriter->writeAttribute('url', $license->getUrl()); - $this->xmlWriter->endElement(); - - $this->xmlWriter->endElement(); - } - - private function addRequirements(RequirementCollection $requirementCollection) { - $phpRequirement = new AnyVersionConstraint(); - $extensions = []; - - foreach($requirementCollection as $requirement) { - if ($requirement instanceof PhpVersionRequirement) { - $phpRequirement = $requirement->getVersionConstraint(); - continue; - } - - if ($requirement instanceof PhpExtensionRequirement) { - $extensions[] = (string) $requirement; - } - } - - $this->xmlWriter->startElement('requires'); - $this->xmlWriter->startElement('php'); - $this->xmlWriter->writeAttribute('version', $phpRequirement->asString()); - - foreach($extensions as $extension) { - $this->xmlWriter->startElement('ext'); - $this->xmlWriter->writeAttribute('name', $extension); - $this->xmlWriter->endElement(); - } - - $this->xmlWriter->endElement(); - $this->xmlWriter->endElement(); - } - - private function addBundles(BundledComponentCollection $bundledComponentCollection) { - if (count($bundledComponentCollection) === 0) { - return; - } - $this->xmlWriter->startElement('bundles'); - - foreach($bundledComponentCollection as $bundledComponent) { - $this->xmlWriter->startElement('component'); - $this->xmlWriter->writeAttribute('name', $bundledComponent->getName()); - $this->xmlWriter->writeAttribute('version', $bundledComponent->getVersion()->getVersionString()); - $this->xmlWriter->endElement(); - } - - $this->xmlWriter->endElement(); - } - - private function addExtension($application, VersionConstraint $versionConstraint) { - $this->xmlWriter->startElement('extension'); - $this->xmlWriter->writeAttribute('for', $application); - $this->xmlWriter->writeAttribute('compatible', $versionConstraint->asString()); - $this->xmlWriter->endElement(); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ManifestLoader { - /** - * @param string $filename - * - * @return Manifest - * - * @throws ManifestLoaderException - */ - public static function fromFile($filename) { - try { - return (new ManifestDocumentMapper())->map( - ManifestDocument::fromFile($filename) - ); - } catch (Exception $e) { - throw new ManifestLoaderException( - sprintf('Loading %s failed.', $filename), - $e->getCode(), - $e - ); - } - } - - /** - * @param string $filename - * - * @return Manifest - * - * @throws ManifestLoaderException - */ - public static function fromPhar($filename) { - return self::fromFile('phar://' . $filename . '/manifest.xml'); - } - - /** - * @param string $manifest - * - * @return Manifest - * - * @throws ManifestLoaderException - */ - public static function fromString($manifest) { - try { - return (new ManifestDocumentMapper())->map( - ManifestDocument::fromString($manifest) - ); - } catch (Exception $e) { - throw new ManifestLoaderException( - 'Processing string failed', - $e->getCode(), - $e - ); - } - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\Version; -use PharIo\Version\Exception as VersionException; -use PharIo\Version\VersionConstraintParser; - -class ManifestDocumentMapper { - /** - * @param ManifestDocument $document - * - * @returns Manifest - * - * @throws ManifestDocumentMapperException - */ - public function map(ManifestDocument $document) { - try { - $contains = $document->getContainsElement(); - $type = $this->mapType($contains); - $copyright = $this->mapCopyright($document->getCopyrightElement()); - $requirements = $this->mapRequirements($document->getRequiresElement()); - $bundledComponents = $this->mapBundledComponents($document); - - return new Manifest( - new ApplicationName($contains->getName()), - new Version($contains->getVersion()), - $type, - $copyright, - $requirements, - $bundledComponents - ); - } catch (VersionException $e) { - throw new ManifestDocumentMapperException($e->getMessage(), $e->getCode(), $e); - } catch (Exception $e) { - throw new ManifestDocumentMapperException($e->getMessage(), $e->getCode(), $e); - } - } - - /** - * @param ContainsElement $contains - * - * @return Type - * - * @throws ManifestDocumentMapperException - */ - private function mapType(ContainsElement $contains) { - switch ($contains->getType()) { - case 'application': - return Type::application(); - case 'library': - return Type::library(); - case 'extension': - return $this->mapExtension($contains->getExtensionElement()); - } - - throw new ManifestDocumentMapperException( - sprintf('Unsupported type %s', $contains->getType()) - ); - } - - /** - * @param CopyrightElement $copyright - * - * @return CopyrightInformation - * - * @throws InvalidUrlException - * @throws InvalidEmailException - */ - private function mapCopyright(CopyrightElement $copyright) { - $authors = new AuthorCollection(); - - foreach($copyright->getAuthorElements() as $authorElement) { - $authors->add( - new Author( - $authorElement->getName(), - new Email($authorElement->getEmail()) - ) - ); - } - - $licenseElement = $copyright->getLicenseElement(); - $license = new License( - $licenseElement->getType(), - new Url($licenseElement->getUrl()) - ); - - return new CopyrightInformation( - $authors, - $license - ); - } - - /** - * @param RequiresElement $requires - * - * @return RequirementCollection - * - * @throws ManifestDocumentMapperException - */ - private function mapRequirements(RequiresElement $requires) { - $collection = new RequirementCollection(); - $phpElement = $requires->getPHPElement(); - $parser = new VersionConstraintParser; - - try { - $versionConstraint = $parser->parse($phpElement->getVersion()); - } catch (VersionException $e) { - throw new ManifestDocumentMapperException( - sprintf('Unsupported version constraint - %s', $e->getMessage()), - $e->getCode(), - $e - ); - } - - $collection->add( - new PhpVersionRequirement( - $versionConstraint - ) - ); - - if (!$phpElement->hasExtElements()) { - return $collection; - } - - foreach($phpElement->getExtElements() as $extElement) { - $collection->add( - new PhpExtensionRequirement($extElement->getName()) - ); - } - - return $collection; - } - - /** - * @param ManifestDocument $document - * - * @return BundledComponentCollection - */ - private function mapBundledComponents(ManifestDocument $document) { - $collection = new BundledComponentCollection(); - - if (!$document->hasBundlesElement()) { - return $collection; - } - - foreach($document->getBundlesElement()->getComponentElements() as $componentElement) { - $collection->add( - new BundledComponent( - $componentElement->getName(), - new Version( - $componentElement->getVersion() - ) - ) - ); - } - - return $collection; - } - - /** - * @param ExtensionElement $extension - * - * @return Extension - * - * @throws ManifestDocumentMapperException - */ - private function mapExtension(ExtensionElement $extension) { - try { - $parser = new VersionConstraintParser; - $versionConstraint = $parser->parse($extension->getCompatible()); - - return Type::extension( - new ApplicationName($extension->getFor()), - $versionConstraint - ); - } catch (VersionException $e) { - throw new ManifestDocumentMapperException( - sprintf('Unsupported version constraint - %s', $e->getMessage()), - $e->getCode(), - $e - ); - } - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class Url { - /** - * @var string - */ - private $url; - - /** - * @param string $url - * - * @throws InvalidUrlException - */ - public function __construct($url) { - $this->ensureUrlIsValid($url); - - $this->url = $url; - } - - /** - * @return string - */ - public function __toString() { - return $this->url; - } - - /** - * @param string $url - * - * @throws InvalidUrlException - */ - private function ensureUrlIsValid($url) { - if (filter_var($url, \FILTER_VALIDATE_URL) === false) { - throw new InvalidUrlException; - } - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class PhpExtensionRequirement implements Requirement { - /** - * @var string - */ - private $extension; - - /** - * @param string $extension - */ - public function __construct($extension) { - $this->extension = $extension; - } - - /** - * @return string - */ - public function __toString() { - return $this->extension; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\Version; - -class Manifest { - /** - * @var ApplicationName - */ - private $name; - - /** - * @var Version - */ - private $version; - - /** - * @var Type - */ - private $type; - - /** - * @var CopyrightInformation - */ - private $copyrightInformation; - - /** - * @var RequirementCollection - */ - private $requirements; - - /** - * @var BundledComponentCollection - */ - private $bundledComponents; - - public function __construct(ApplicationName $name, Version $version, Type $type, CopyrightInformation $copyrightInformation, RequirementCollection $requirements, BundledComponentCollection $bundledComponents) { - $this->name = $name; - $this->version = $version; - $this->type = $type; - $this->copyrightInformation = $copyrightInformation; - $this->requirements = $requirements; - $this->bundledComponents = $bundledComponents; - } - - /** - * @return ApplicationName - */ - public function getName() { - return $this->name; - } - - /** - * @return Version - */ - public function getVersion() { - return $this->version; - } - - /** - * @return Type - */ - public function getType() { - return $this->type; - } - - /** - * @return CopyrightInformation - */ - public function getCopyrightInformation() { - return $this->copyrightInformation; - } - - /** - * @return RequirementCollection - */ - public function getRequirements() { - return $this->requirements; - } - - /** - * @return BundledComponentCollection - */ - public function getBundledComponents() { - return $this->bundledComponents; - } - - /** - * @return bool - */ - public function isApplication() { - return $this->type->isApplication(); - } - - /** - * @return bool - */ - public function isLibrary() { - return $this->type->isLibrary(); - } - - /** - * @return bool - */ - public function isExtension() { - return $this->type->isExtension(); - } - - /** - * @param ApplicationName $application - * @param Version|null $version - * - * @return bool - */ - public function isExtensionFor(ApplicationName $application, Version $version = null) { - if (!$this->isExtension()) { - return false; - } - - /** @var Extension $type */ - $type = $this->type; - - if ($version !== null) { - return $type->isCompatibleWith($application, $version); - } - - return $type->isExtensionFor($application); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class BundledComponentCollectionIterator implements \Iterator { - /** - * @var BundledComponent[] - */ - private $bundledComponents = []; - - /** - * @var int - */ - private $position; - - public function __construct(BundledComponentCollection $bundledComponents) { - $this->bundledComponents = $bundledComponents->getBundledComponents(); - } - - public function rewind() { - $this->position = 0; - } - - /** - * @return bool - */ - public function valid() { - return $this->position < count($this->bundledComponents); - } - - /** - * @return int - */ - public function key() { - return $this->position; - } - - /** - * @return BundledComponent - */ - public function current() { - return $this->bundledComponents[$this->position]; - } - - public function next() { - $this->position++; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class Application extends Type { - /** - * @return bool - */ - public function isApplication() { - return true; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\Version; - -class BundledComponent { - /** - * @var string - */ - private $name; - - /** - * @var Version - */ - private $version; - - /** - * @param string $name - * @param Version $version - */ - public function __construct($name, Version $version) { - $this->name = $name; - $this->version = $version; - } - - /** - * @return string - */ - public function getName() { - return $this->name; - } - - /** - * @return Version - */ - public function getVersion() { - return $this->version; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class RequirementCollection implements \Countable, \IteratorAggregate { - /** - * @var Requirement[] - */ - private $requirements = []; - - public function add(Requirement $requirement) { - $this->requirements[] = $requirement; - } - - /** - * @return Requirement[] - */ - public function getRequirements() { - return $this->requirements; - } - - /** - * @return int - */ - public function count() { - return count($this->requirements); - } - - /** - * @return RequirementCollectionIterator - */ - public function getIterator() { - return new RequirementCollectionIterator($this); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class License { - /** - * @var string - */ - private $name; - - /** - * @var Url - */ - private $url; - - public function __construct($name, Url $url) { - $this->name = $name; - $this->url = $url; - } - - /** - * @return string - */ - public function getName() { - return $this->name; - } - - /** - * @return Url - */ - public function getUrl() { - return $this->url; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class Library extends Type { - /** - * @return bool - */ - public function isLibrary() { - return true; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -interface Requirement { -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class Author { - /** - * @var string - */ - private $name; - - /** - * @var Email - */ - private $email; - - /** - * @param string $name - * @param Email $email - */ - public function __construct($name, Email $email) { - $this->name = $name; - $this->email = $email; - } - - /** - * @return string - */ - public function getName() { - return $this->name; - } - - /** - * @return Email - */ - public function getEmail() { - return $this->email; - } - - /** - * @return string - */ - public function __toString() { - return sprintf( - '%s <%s>', - $this->name, - $this->email - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\VersionConstraint; - -class PhpVersionRequirement implements Requirement { - /** - * @var VersionConstraint - */ - private $versionConstraint; - - public function __construct(VersionConstraint $versionConstraint) { - $this->versionConstraint = $versionConstraint; - } - - /** - * @return VersionConstraint - */ - public function getVersionConstraint() { - return $this->versionConstraint; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class BundledComponentCollection implements \Countable, \IteratorAggregate { - /** - * @var BundledComponent[] - */ - private $bundledComponents = []; - - public function add(BundledComponent $bundledComponent) { - $this->bundledComponents[] = $bundledComponent; - } - - /** - * @return BundledComponent[] - */ - public function getBundledComponents() { - return $this->bundledComponents; - } - - /** - * @return int - */ - public function count() { - return count($this->bundledComponents); - } - - /** - * @return BundledComponentCollectionIterator - */ - public function getIterator() { - return new BundledComponentCollectionIterator($this); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class CopyrightInformation { - /** - * @var AuthorCollection - */ - private $authors; - - /** - * @var License - */ - private $license; - - public function __construct(AuthorCollection $authors, License $license) { - $this->authors = $authors; - $this->license = $license; - } - - /** - * @return AuthorCollection - */ - public function getAuthors() { - return $this->authors; - } - - /** - * @return License - */ - public function getLicense() { - return $this->license; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\Version; -use PharIo\Version\VersionConstraint; - -class Extension extends Type { - /** - * @var ApplicationName - */ - private $application; - - /** - * @var VersionConstraint - */ - private $versionConstraint; - - /** - * @param ApplicationName $application - * @param VersionConstraint $versionConstraint - */ - public function __construct(ApplicationName $application, VersionConstraint $versionConstraint) { - $this->application = $application; - $this->versionConstraint = $versionConstraint; - } - - /** - * @return ApplicationName - */ - public function getApplicationName() { - return $this->application; - } - - /** - * @return VersionConstraint - */ - public function getVersionConstraint() { - return $this->versionConstraint; - } - - /** - * @return bool - */ - public function isExtension() { - return true; - } - - /** - * @param ApplicationName $name - * - * @return bool - */ - public function isExtensionFor(ApplicationName $name) { - return $this->application->isEqual($name); - } - - /** - * @param ApplicationName $name - * @param Version $version - * - * @return bool - */ - public function isCompatibleWith(ApplicationName $name, Version $version) { - return $this->isExtensionFor($name) && $this->versionConstraint->complies($version); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -use PharIo\Version\VersionConstraint; - -abstract class Type { - /** - * @return Application - */ - public static function application() { - return new Application; - } - - /** - * @return Library - */ - public static function library() { - return new Library; - } - - /** - * @param ApplicationName $application - * @param VersionConstraint $versionConstraint - * - * @return Extension - */ - public static function extension(ApplicationName $application, VersionConstraint $versionConstraint) { - return new Extension($application, $versionConstraint); - } - - /** - * @return bool - */ - public function isApplication() { - return false; - } - - /** - * @return bool - */ - public function isLibrary() { - return false; - } - - /** - * @return bool - */ - public function isExtension() { - return false; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class AuthorCollectionIterator implements \Iterator { - /** - * @var Author[] - */ - private $authors = []; - - /** - * @var int - */ - private $position; - - public function __construct(AuthorCollection $authors) { - $this->authors = $authors->getAuthors(); - } - - public function rewind() { - $this->position = 0; - } - - /** - * @return bool - */ - public function valid() { - return $this->position < count($this->authors); - } - - /** - * @return int - */ - public function key() { - return $this->position; - } - - /** - * @return Author - */ - public function current() { - return $this->authors[$this->position]; - } - - public function next() { - $this->position++; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class Email { - /** - * @var string - */ - private $email; - - /** - * @param string $email - * - * @throws InvalidEmailException - */ - public function __construct($email) { - $this->ensureEmailIsValid($email); - - $this->email = $email; - } - - /** - * @return string - */ - public function __toString() { - return $this->email; - } - - /** - * @param string $url - * - * @throws InvalidEmailException - */ - private function ensureEmailIsValid($url) { - if (filter_var($url, \FILTER_VALIDATE_EMAIL) === false) { - throw new InvalidEmailException; - } - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class RequirementCollectionIterator implements \Iterator { - /** - * @var Requirement[] - */ - private $requirements = []; - - /** - * @var int - */ - private $position; - - public function __construct(RequirementCollection $requirements) { - $this->requirements = $requirements->getRequirements(); - } - - public function rewind() { - $this->position = 0; - } - - /** - * @return bool - */ - public function valid() { - return $this->position < count($this->requirements); - } - - /** - * @return int - */ - public function key() { - return $this->position; - } - - /** - * @return Requirement - */ - public function current() { - return $this->requirements[$this->position]; - } - - public function next() { - $this->position++; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class AuthorCollection implements \Countable, \IteratorAggregate { - /** - * @var Author[] - */ - private $authors = []; - - public function add(Author $author) { - $this->authors[] = $author; - } - - /** - * @return Author[] - */ - public function getAuthors() { - return $this->authors; - } - - /** - * @return int - */ - public function count() { - return count($this->authors); - } - - /** - * @return AuthorCollectionIterator - */ - public function getIterator() { - return new AuthorCollectionIterator($this); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class ApplicationName { - /** - * @var string - */ - private $name; - - /** - * ApplicationName constructor. - * - * @param string $name - * - * @throws InvalidApplicationNameException - */ - public function __construct($name) { - $this->ensureIsString($name); - $this->ensureValidFormat($name); - $this->name = $name; - } - - /** - * @return string - */ - public function __toString() { - return $this->name; - } - - public function isEqual(ApplicationName $name) { - return $this->name === $name->name; - } - - /** - * @param string $name - * - * @throws InvalidApplicationNameException - */ - private function ensureValidFormat($name) { - if (!preg_match('#\w/\w#', $name)) { - throw new InvalidApplicationNameException( - sprintf('Format of name "%s" is not valid - expected: vendor/packagename', $name), - InvalidApplicationNameException::InvalidFormat - ); - } - } - - private function ensureIsString($name) { - if (!is_string($name)) { - throw new InvalidApplicationNameException( - 'Name must be a string', - InvalidApplicationNameException::NotAString - ); - } - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class InvalidEmailException extends \InvalidArgumentException implements Exception { -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -interface Exception { -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class InvalidApplicationNameException extends \InvalidArgumentException implements Exception { - const NotAString = 1; - const InvalidFormat = 2; -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Manifest; - -class InvalidUrlException extends \InvalidArgumentException implements Exception { -} -, Sebastian Heuer , Sebastian Bergmann , and contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\FileIterator; - -class Iterator extends \FilterIterator -{ - const PREFIX = 0; - const SUFFIX = 1; - - /** - * @var string - */ - private $basePath; - - /** - * @var array - */ - private $suffixes = []; - - /** - * @var array - */ - private $prefixes = []; - - /** - * @var array - */ - private $exclude = []; - - /** - * @param string $basePath - * @param \Iterator $iterator - * @param array $suffixes - * @param array $prefixes - * @param array $exclude - */ - public function __construct(string $basePath, \Iterator $iterator, array $suffixes = [], array $prefixes = [], array $exclude = []) - { - $this->basePath = \realpath($basePath); - $this->prefixes = $prefixes; - $this->suffixes = $suffixes; - $this->exclude = \array_filter(\array_map('realpath', $exclude)); - - parent::__construct($iterator); - } - - public function accept() - { - $current = $this->getInnerIterator()->current(); - $filename = $current->getFilename(); - $realPath = $current->getRealPath(); - - return $this->acceptPath($realPath) && - $this->acceptPrefix($filename) && - $this->acceptSuffix($filename); - } - - private function acceptPath(string $path): bool - { - // Filter files in hidden directories by checking path that is relative to the base path. - if (\preg_match('=/\.[^/]*/=', \str_replace($this->basePath, '', $path))) { - return false; - } - - foreach ($this->exclude as $exclude) { - if (\strpos($path, $exclude) === 0) { - return false; - } - } - - return true; - } - - private function acceptPrefix(string $filename): bool - { - return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); - } - - private function acceptSuffix(string $filename): bool - { - return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); - } - - private function acceptSubString(string $filename, array $subStrings, int $type): bool - { - if (empty($subStrings)) { - return true; - } - - $matched = false; - - foreach ($subStrings as $string) { - if (($type === self::PREFIX && \strpos($filename, $string) === 0) || - ($type === self::SUFFIX && - \substr($filename, -1 * \strlen($string)) === $string)) { - $matched = true; - - break; - } - } - - return $matched; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\FileIterator; - -class Facade -{ - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - * @param array $exclude - * @param bool $commonPath - * - * @return array - */ - public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = [], bool $commonPath = false): array - { - if (\is_string($paths)) { - $paths = [$paths]; - } - - $factory = new Factory; - - $iterator = $factory->getFileIterator($paths, $suffixes, $prefixes, $exclude); - - $files = []; - - foreach ($iterator as $file) { - $file = $file->getRealPath(); - - if ($file) { - $files[] = $file; - } - } - - foreach ($paths as $path) { - if (\is_file($path)) { - $files[] = \realpath($path); - } - } - - $files = \array_unique($files); - \sort($files); - - if ($commonPath) { - return [ - 'commonPath' => $this->getCommonPath($files), - 'files' => $files - ]; - } - - return $files; - } - - protected function getCommonPath(array $files): string - { - $count = \count($files); - - if ($count === 0) { - return ''; - } - - if ($count === 1) { - return \dirname($files[0]) . DIRECTORY_SEPARATOR; - } - - $_files = []; - - foreach ($files as $file) { - $_files[] = $_fileParts = \explode(DIRECTORY_SEPARATOR, $file); - - if (empty($_fileParts[0])) { - $_fileParts[0] = DIRECTORY_SEPARATOR; - } - } - - $common = ''; - $done = false; - $j = 0; - $count--; - - while (!$done) { - for ($i = 0; $i < $count; $i++) { - if ($_files[$i][$j] != $_files[$i + 1][$j]) { - $done = true; - - break; - } - } - - if (!$done) { - $common .= $_files[0][$j]; - - if ($j > 0) { - $common .= DIRECTORY_SEPARATOR; - } - } - - $j++; - } - - return DIRECTORY_SEPARATOR . $common; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\FileIterator; - -class Factory -{ - /** - * @param array|string $paths - * @param array|string $suffixes - * @param array|string $prefixes - * @param array $exclude - * - * @return \AppendIterator - */ - public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = []): \AppendIterator - { - if (\is_string($paths)) { - $paths = [$paths]; - } - - $paths = $this->getPathsAfterResolvingWildcards($paths); - $exclude = $this->getPathsAfterResolvingWildcards($exclude); - - if (\is_string($prefixes)) { - if ($prefixes !== '') { - $prefixes = [$prefixes]; - } else { - $prefixes = []; - } - } - - if (\is_string($suffixes)) { - if ($suffixes !== '') { - $suffixes = [$suffixes]; - } else { - $suffixes = []; - } - } - - $iterator = new \AppendIterator; - - foreach ($paths as $path) { - if (\is_dir($path)) { - $iterator->append( - new Iterator( - $path, - new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::FOLLOW_SYMLINKS | \RecursiveDirectoryIterator::SKIP_DOTS) - ), - $suffixes, - $prefixes, - $exclude - ) - ); - } - } - - return $iterator; - } - - protected function getPathsAfterResolvingWildcards(array $paths): array - { - $_paths = []; - - foreach ($paths as $path) { - if ($locals = \glob($path, GLOB_ONLYDIR)) { - $_paths = \array_merge($_paths, \array_map('\realpath', $locals)); - } else { - $_paths[] = \realpath($path); - } - } - - return \array_filter($_paths); - } -} -php-file-iterator - -Copyright (c) 2009-2018, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * A PHP token. - */ -abstract class PHP_Token -{ - /** - * @var string - */ - protected $text; - - /** - * @var int - */ - protected $line; - - /** - * @var PHP_Token_Stream - */ - protected $tokenStream; - - /** - * @var int - */ - protected $id; - - /** - * @param string $text - * @param int $line - * @param PHP_Token_Stream $tokenStream - * @param int $id - */ - public function __construct($text, $line, PHP_Token_Stream $tokenStream, $id) - { - $this->text = $text; - $this->line = $line; - $this->tokenStream = $tokenStream; - $this->id = $id; - } - - /** - * @return string - */ - public function __toString() - { - return $this->text; - } - - /** - * @return int - */ - public function getLine() - { - return $this->line; - } - - /** - * @return int - */ - public function getId() - { - return $this->id; - } -} - -abstract class PHP_TokenWithScope extends PHP_Token -{ - /** - * @var int - */ - protected $endTokenId; - - /** - * Get the docblock for this token - * - * This method will fetch the docblock belonging to the current token. The - * docblock must be placed on the line directly above the token to be - * recognized. - * - * @return string|null Returns the docblock as a string if found - */ - public function getDocblock() - { - $tokens = $this->tokenStream->tokens(); - $currentLineNumber = $tokens[$this->id]->getLine(); - $prevLineNumber = $currentLineNumber - 1; - - for ($i = $this->id - 1; $i; $i--) { - if (!isset($tokens[$i])) { - return; - } - - if ($tokens[$i] instanceof PHP_Token_FUNCTION || - $tokens[$i] instanceof PHP_Token_CLASS || - $tokens[$i] instanceof PHP_Token_TRAIT) { - // Some other trait, class or function, no docblock can be - // used for the current token - break; - } - - $line = $tokens[$i]->getLine(); - - if ($line == $currentLineNumber || - ($line == $prevLineNumber && - $tokens[$i] instanceof PHP_Token_WHITESPACE)) { - continue; - } - - if ($line < $currentLineNumber && - !$tokens[$i] instanceof PHP_Token_DOC_COMMENT) { - break; - } - - return (string) $tokens[$i]; - } - } - - /** - * @return int - */ - public function getEndTokenId() - { - $block = 0; - $i = $this->id; - $tokens = $this->tokenStream->tokens(); - - while ($this->endTokenId === null && isset($tokens[$i])) { - if ($tokens[$i] instanceof PHP_Token_OPEN_CURLY || - $tokens[$i] instanceof PHP_Token_DOLLAR_OPEN_CURLY_BRACES || - $tokens[$i] instanceof PHP_Token_CURLY_OPEN) { - $block++; - } elseif ($tokens[$i] instanceof PHP_Token_CLOSE_CURLY) { - $block--; - - if ($block === 0) { - $this->endTokenId = $i; - } - } elseif (($this instanceof PHP_Token_FUNCTION || - $this instanceof PHP_Token_NAMESPACE) && - $tokens[$i] instanceof PHP_Token_SEMICOLON) { - if ($block === 0) { - $this->endTokenId = $i; - } - } - - $i++; - } - - if ($this->endTokenId === null) { - $this->endTokenId = $this->id; - } - - return $this->endTokenId; - } - - /** - * @return int - */ - public function getEndLine() - { - return $this->tokenStream[$this->getEndTokenId()]->getLine(); - } -} - -abstract class PHP_TokenWithScopeAndVisibility extends PHP_TokenWithScope -{ - /** - * @return string - */ - public function getVisibility() - { - $tokens = $this->tokenStream->tokens(); - - for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { - if (isset($tokens[$i]) && - ($tokens[$i] instanceof PHP_Token_PRIVATE || - $tokens[$i] instanceof PHP_Token_PROTECTED || - $tokens[$i] instanceof PHP_Token_PUBLIC)) { - return strtolower( - str_replace('PHP_Token_', '', get_class($tokens[$i])) - ); - } - if (isset($tokens[$i]) && - !($tokens[$i] instanceof PHP_Token_STATIC || - $tokens[$i] instanceof PHP_Token_FINAL || - $tokens[$i] instanceof PHP_Token_ABSTRACT)) { - // no keywords; stop visibility search - break; - } - } - } - - /** - * @return string - */ - public function getKeywords() - { - $keywords = []; - $tokens = $this->tokenStream->tokens(); - - for ($i = $this->id - 2; $i > $this->id - 7; $i -= 2) { - if (isset($tokens[$i]) && - ($tokens[$i] instanceof PHP_Token_PRIVATE || - $tokens[$i] instanceof PHP_Token_PROTECTED || - $tokens[$i] instanceof PHP_Token_PUBLIC)) { - continue; - } - - if (isset($tokens[$i]) && - ($tokens[$i] instanceof PHP_Token_STATIC || - $tokens[$i] instanceof PHP_Token_FINAL || - $tokens[$i] instanceof PHP_Token_ABSTRACT)) { - $keywords[] = strtolower( - str_replace('PHP_Token_', '', get_class($tokens[$i])) - ); - } - } - - return implode(',', $keywords); - } -} - -abstract class PHP_Token_Includes extends PHP_Token -{ - /** - * @var string - */ - protected $name; - - /** - * @var string - */ - protected $type; - - /** - * @return string - */ - public function getName() - { - if ($this->name === null) { - $this->process(); - } - - return $this->name; - } - - /** - * @return string - */ - public function getType() - { - if ($this->type === null) { - $this->process(); - } - - return $this->type; - } - - private function process() - { - $tokens = $this->tokenStream->tokens(); - - if ($tokens[$this->id + 2] instanceof PHP_Token_CONSTANT_ENCAPSED_STRING) { - $this->name = trim($tokens[$this->id + 2], "'\""); - $this->type = strtolower( - str_replace('PHP_Token_', '', get_class($tokens[$this->id])) - ); - } - } -} - -class PHP_Token_FUNCTION extends PHP_TokenWithScopeAndVisibility -{ - /** - * @var array - */ - protected $arguments; - - /** - * @var int - */ - protected $ccn; - - /** - * @var string - */ - protected $name; - - /** - * @var string - */ - protected $signature; - - /** - * @var bool - */ - private $anonymous = false; - - /** - * @return array - */ - public function getArguments() - { - if ($this->arguments !== null) { - return $this->arguments; - } - - $this->arguments = []; - $tokens = $this->tokenStream->tokens(); - $typeDeclaration = null; - - // Search for first token inside brackets - $i = $this->id + 2; - - while (!$tokens[$i - 1] instanceof PHP_Token_OPEN_BRACKET) { - $i++; - } - - while (!$tokens[$i] instanceof PHP_Token_CLOSE_BRACKET) { - if ($tokens[$i] instanceof PHP_Token_STRING) { - $typeDeclaration = (string) $tokens[$i]; - } elseif ($tokens[$i] instanceof PHP_Token_VARIABLE) { - $this->arguments[(string) $tokens[$i]] = $typeDeclaration; - $typeDeclaration = null; - } - - $i++; - } - - return $this->arguments; - } - - /** - * @return string - */ - public function getName() - { - if ($this->name !== null) { - return $this->name; - } - - $tokens = $this->tokenStream->tokens(); - - $i = $this->id + 1; - - if ($tokens[$i] instanceof PHP_Token_WHITESPACE) { - $i++; - } - - if ($tokens[$i] instanceof PHP_Token_AMPERSAND) { - $i++; - } - - if ($tokens[$i + 1] instanceof PHP_Token_OPEN_BRACKET) { - $this->name = (string) $tokens[$i]; - } elseif ($tokens[$i + 1] instanceof PHP_Token_WHITESPACE && $tokens[$i + 2] instanceof PHP_Token_OPEN_BRACKET) { - $this->name = (string) $tokens[$i]; - } else { - $this->anonymous = true; - - $this->name = sprintf( - 'anonymousFunction:%s#%s', - $this->getLine(), - $this->getId() - ); - } - - if (!$this->isAnonymous()) { - for ($i = $this->id; $i; --$i) { - if ($tokens[$i] instanceof PHP_Token_NAMESPACE) { - $this->name = $tokens[$i]->getName() . '\\' . $this->name; - - break; - } - - if ($tokens[$i] instanceof PHP_Token_INTERFACE) { - break; - } - } - } - - return $this->name; - } - - /** - * @return int - */ - public function getCCN() - { - if ($this->ccn !== null) { - return $this->ccn; - } - - $this->ccn = 1; - $end = $this->getEndTokenId(); - $tokens = $this->tokenStream->tokens(); - - for ($i = $this->id; $i <= $end; $i++) { - switch (get_class($tokens[$i])) { - case 'PHP_Token_IF': - case 'PHP_Token_ELSEIF': - case 'PHP_Token_FOR': - case 'PHP_Token_FOREACH': - case 'PHP_Token_WHILE': - case 'PHP_Token_CASE': - case 'PHP_Token_CATCH': - case 'PHP_Token_BOOLEAN_AND': - case 'PHP_Token_LOGICAL_AND': - case 'PHP_Token_BOOLEAN_OR': - case 'PHP_Token_LOGICAL_OR': - case 'PHP_Token_QUESTION_MARK': - $this->ccn++; - break; - } - } - - return $this->ccn; - } - - /** - * @return string - */ - public function getSignature() - { - if ($this->signature !== null) { - return $this->signature; - } - - if ($this->isAnonymous()) { - $this->signature = 'anonymousFunction'; - $i = $this->id + 1; - } else { - $this->signature = ''; - $i = $this->id + 2; - } - - $tokens = $this->tokenStream->tokens(); - - while (isset($tokens[$i]) && - !$tokens[$i] instanceof PHP_Token_OPEN_CURLY && - !$tokens[$i] instanceof PHP_Token_SEMICOLON) { - $this->signature .= $tokens[$i++]; - } - - $this->signature = trim($this->signature); - - return $this->signature; - } - - /** - * @return bool - */ - public function isAnonymous() - { - return $this->anonymous; - } -} - -class PHP_Token_INTERFACE extends PHP_TokenWithScopeAndVisibility -{ - /** - * @var array - */ - protected $interfaces; - - /** - * @return string - */ - public function getName() - { - return (string) $this->tokenStream[$this->id + 2]; - } - - /** - * @return bool - */ - public function hasParent() - { - return $this->tokenStream[$this->id + 4] instanceof PHP_Token_EXTENDS; - } - - /** - * @return array - */ - public function getPackage() - { - $className = $this->getName(); - $docComment = $this->getDocblock(); - - $result = [ - 'namespace' => '', - 'fullPackage' => '', - 'category' => '', - 'package' => '', - 'subpackage' => '' - ]; - - for ($i = $this->id; $i; --$i) { - if ($this->tokenStream[$i] instanceof PHP_Token_NAMESPACE) { - $result['namespace'] = $this->tokenStream[$i]->getName(); - break; - } - } - - if (preg_match('/@category[\s]+([\.\w]+)/', $docComment, $matches)) { - $result['category'] = $matches[1]; - } - - if (preg_match('/@package[\s]+([\.\w]+)/', $docComment, $matches)) { - $result['package'] = $matches[1]; - $result['fullPackage'] = $matches[1]; - } - - if (preg_match('/@subpackage[\s]+([\.\w]+)/', $docComment, $matches)) { - $result['subpackage'] = $matches[1]; - $result['fullPackage'] .= '.' . $matches[1]; - } - - if (empty($result['fullPackage'])) { - $result['fullPackage'] = $this->arrayToName( - explode('_', str_replace('\\', '_', $className)), - '.' - ); - } - - return $result; - } - - /** - * @param array $parts - * @param string $join - * - * @return string - */ - protected function arrayToName(array $parts, $join = '\\') - { - $result = ''; - - if (count($parts) > 1) { - array_pop($parts); - - $result = implode($join, $parts); - } - - return $result; - } - - /** - * @return bool|string - */ - public function getParent() - { - if (!$this->hasParent()) { - return false; - } - - $i = $this->id + 6; - $tokens = $this->tokenStream->tokens(); - $className = (string) $tokens[$i]; - - while (isset($tokens[$i + 1]) && - !$tokens[$i + 1] instanceof PHP_Token_WHITESPACE) { - $className .= (string) $tokens[++$i]; - } - - return $className; - } - - /** - * @return bool - */ - public function hasInterfaces() - { - return (isset($this->tokenStream[$this->id + 4]) && - $this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) || - (isset($this->tokenStream[$this->id + 8]) && - $this->tokenStream[$this->id + 8] instanceof PHP_Token_IMPLEMENTS); - } - - /** - * @return array|bool - */ - public function getInterfaces() - { - if ($this->interfaces !== null) { - return $this->interfaces; - } - - if (!$this->hasInterfaces()) { - return ($this->interfaces = false); - } - - if ($this->tokenStream[$this->id + 4] instanceof PHP_Token_IMPLEMENTS) { - $i = $this->id + 3; - } else { - $i = $this->id + 7; - } - - $tokens = $this->tokenStream->tokens(); - - while (!$tokens[$i + 1] instanceof PHP_Token_OPEN_CURLY) { - $i++; - - if ($tokens[$i] instanceof PHP_Token_STRING) { - $this->interfaces[] = (string) $tokens[$i]; - } - } - - return $this->interfaces; - } -} - -class PHP_Token_ABSTRACT extends PHP_Token -{ -} - -class PHP_Token_AMPERSAND extends PHP_Token -{ -} - -class PHP_Token_AND_EQUAL extends PHP_Token -{ -} - -class PHP_Token_ARRAY extends PHP_Token -{ -} - -class PHP_Token_ARRAY_CAST extends PHP_Token -{ -} - -class PHP_Token_AS extends PHP_Token -{ -} - -class PHP_Token_AT extends PHP_Token -{ -} - -class PHP_Token_BACKTICK extends PHP_Token -{ -} - -class PHP_Token_BAD_CHARACTER extends PHP_Token -{ -} - -class PHP_Token_BOOLEAN_AND extends PHP_Token -{ -} - -class PHP_Token_BOOLEAN_OR extends PHP_Token -{ -} - -class PHP_Token_BOOL_CAST extends PHP_Token -{ -} - -class PHP_Token_BREAK extends PHP_Token -{ -} - -class PHP_Token_CARET extends PHP_Token -{ -} - -class PHP_Token_CASE extends PHP_Token -{ -} - -class PHP_Token_CATCH extends PHP_Token -{ -} - -class PHP_Token_CHARACTER extends PHP_Token -{ -} - -class PHP_Token_CLASS extends PHP_Token_INTERFACE -{ - /** - * @var bool - */ - private $anonymous = false; - - /** - * @var string - */ - private $name; - - /** - * @return string - */ - public function getName() - { - if ($this->name !== null) { - return $this->name; - } - - $next = $this->tokenStream[$this->id + 1]; - - if ($next instanceof PHP_Token_WHITESPACE) { - $next = $this->tokenStream[$this->id + 2]; - } - - if ($next instanceof PHP_Token_STRING) { - $this->name =(string) $next; - - return $this->name; - } - - if ($next instanceof PHP_Token_OPEN_CURLY || - $next instanceof PHP_Token_EXTENDS || - $next instanceof PHP_Token_IMPLEMENTS) { - - $this->name = sprintf( - 'AnonymousClass:%s#%s', - $this->getLine(), - $this->getId() - ); - - $this->anonymous = true; - - return $this->name; - } - } - - public function isAnonymous() - { - return $this->anonymous; - } -} - -class PHP_Token_CLASS_C extends PHP_Token -{ -} - -class PHP_Token_CLASS_NAME_CONSTANT extends PHP_Token -{ -} - -class PHP_Token_CLONE extends PHP_Token -{ -} - -class PHP_Token_CLOSE_BRACKET extends PHP_Token -{ -} - -class PHP_Token_CLOSE_CURLY extends PHP_Token -{ -} - -class PHP_Token_CLOSE_SQUARE extends PHP_Token -{ -} - -class PHP_Token_CLOSE_TAG extends PHP_Token -{ -} - -class PHP_Token_COLON extends PHP_Token -{ -} - -class PHP_Token_COMMA extends PHP_Token -{ -} - -class PHP_Token_COMMENT extends PHP_Token -{ -} - -class PHP_Token_CONCAT_EQUAL extends PHP_Token -{ -} - -class PHP_Token_CONST extends PHP_Token -{ -} - -class PHP_Token_CONSTANT_ENCAPSED_STRING extends PHP_Token -{ -} - -class PHP_Token_CONTINUE extends PHP_Token -{ -} - -class PHP_Token_CURLY_OPEN extends PHP_Token -{ -} - -class PHP_Token_DEC extends PHP_Token -{ -} - -class PHP_Token_DECLARE extends PHP_Token -{ -} - -class PHP_Token_DEFAULT extends PHP_Token -{ -} - -class PHP_Token_DIV extends PHP_Token -{ -} - -class PHP_Token_DIV_EQUAL extends PHP_Token -{ -} - -class PHP_Token_DNUMBER extends PHP_Token -{ -} - -class PHP_Token_DO extends PHP_Token -{ -} - -class PHP_Token_DOC_COMMENT extends PHP_Token -{ -} - -class PHP_Token_DOLLAR extends PHP_Token -{ -} - -class PHP_Token_DOLLAR_OPEN_CURLY_BRACES extends PHP_Token -{ -} - -class PHP_Token_DOT extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_ARROW extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_CAST extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_COLON extends PHP_Token -{ -} - -class PHP_Token_DOUBLE_QUOTES extends PHP_Token -{ -} - -class PHP_Token_ECHO extends PHP_Token -{ -} - -class PHP_Token_ELSE extends PHP_Token -{ -} - -class PHP_Token_ELSEIF extends PHP_Token -{ -} - -class PHP_Token_EMPTY extends PHP_Token -{ -} - -class PHP_Token_ENCAPSED_AND_WHITESPACE extends PHP_Token -{ -} - -class PHP_Token_ENDDECLARE extends PHP_Token -{ -} - -class PHP_Token_ENDFOR extends PHP_Token -{ -} - -class PHP_Token_ENDFOREACH extends PHP_Token -{ -} - -class PHP_Token_ENDIF extends PHP_Token -{ -} - -class PHP_Token_ENDSWITCH extends PHP_Token -{ -} - -class PHP_Token_ENDWHILE extends PHP_Token -{ -} - -class PHP_Token_END_HEREDOC extends PHP_Token -{ -} - -class PHP_Token_EQUAL extends PHP_Token -{ -} - -class PHP_Token_EVAL extends PHP_Token -{ -} - -class PHP_Token_EXCLAMATION_MARK extends PHP_Token -{ -} - -class PHP_Token_EXIT extends PHP_Token -{ -} - -class PHP_Token_EXTENDS extends PHP_Token -{ -} - -class PHP_Token_FILE extends PHP_Token -{ -} - -class PHP_Token_FINAL extends PHP_Token -{ -} - -class PHP_Token_FOR extends PHP_Token -{ -} - -class PHP_Token_FOREACH extends PHP_Token -{ -} - -class PHP_Token_FUNC_C extends PHP_Token -{ -} - -class PHP_Token_GLOBAL extends PHP_Token -{ -} - -class PHP_Token_GT extends PHP_Token -{ -} - -class PHP_Token_IF extends PHP_Token -{ -} - -class PHP_Token_IMPLEMENTS extends PHP_Token -{ -} - -class PHP_Token_INC extends PHP_Token -{ -} - -class PHP_Token_INCLUDE extends PHP_Token_Includes -{ -} - -class PHP_Token_INCLUDE_ONCE extends PHP_Token_Includes -{ -} - -class PHP_Token_INLINE_HTML extends PHP_Token -{ -} - -class PHP_Token_INSTANCEOF extends PHP_Token -{ -} - -class PHP_Token_INT_CAST extends PHP_Token -{ -} - -class PHP_Token_ISSET extends PHP_Token -{ -} - -class PHP_Token_IS_EQUAL extends PHP_Token -{ -} - -class PHP_Token_IS_GREATER_OR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_IS_IDENTICAL extends PHP_Token -{ -} - -class PHP_Token_IS_NOT_EQUAL extends PHP_Token -{ -} - -class PHP_Token_IS_NOT_IDENTICAL extends PHP_Token -{ -} - -class PHP_Token_IS_SMALLER_OR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_LINE extends PHP_Token -{ -} - -class PHP_Token_LIST extends PHP_Token -{ -} - -class PHP_Token_LNUMBER extends PHP_Token -{ -} - -class PHP_Token_LOGICAL_AND extends PHP_Token -{ -} - -class PHP_Token_LOGICAL_OR extends PHP_Token -{ -} - -class PHP_Token_LOGICAL_XOR extends PHP_Token -{ -} - -class PHP_Token_LT extends PHP_Token -{ -} - -class PHP_Token_METHOD_C extends PHP_Token -{ -} - -class PHP_Token_MINUS extends PHP_Token -{ -} - -class PHP_Token_MINUS_EQUAL extends PHP_Token -{ -} - -class PHP_Token_MOD_EQUAL extends PHP_Token -{ -} - -class PHP_Token_MULT extends PHP_Token -{ -} - -class PHP_Token_MUL_EQUAL extends PHP_Token -{ -} - -class PHP_Token_NEW extends PHP_Token -{ -} - -class PHP_Token_NUM_STRING extends PHP_Token -{ -} - -class PHP_Token_OBJECT_CAST extends PHP_Token -{ -} - -class PHP_Token_OBJECT_OPERATOR extends PHP_Token -{ -} - -class PHP_Token_OPEN_BRACKET extends PHP_Token -{ -} - -class PHP_Token_OPEN_CURLY extends PHP_Token -{ -} - -class PHP_Token_OPEN_SQUARE extends PHP_Token -{ -} - -class PHP_Token_OPEN_TAG extends PHP_Token -{ -} - -class PHP_Token_OPEN_TAG_WITH_ECHO extends PHP_Token -{ -} - -class PHP_Token_OR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_PAAMAYIM_NEKUDOTAYIM extends PHP_Token -{ -} - -class PHP_Token_PERCENT extends PHP_Token -{ -} - -class PHP_Token_PIPE extends PHP_Token -{ -} - -class PHP_Token_PLUS extends PHP_Token -{ -} - -class PHP_Token_PLUS_EQUAL extends PHP_Token -{ -} - -class PHP_Token_PRINT extends PHP_Token -{ -} - -class PHP_Token_PRIVATE extends PHP_Token -{ -} - -class PHP_Token_PROTECTED extends PHP_Token -{ -} - -class PHP_Token_PUBLIC extends PHP_Token -{ -} - -class PHP_Token_QUESTION_MARK extends PHP_Token -{ -} - -class PHP_Token_REQUIRE extends PHP_Token_Includes -{ -} - -class PHP_Token_REQUIRE_ONCE extends PHP_Token_Includes -{ -} - -class PHP_Token_RETURN extends PHP_Token -{ -} - -class PHP_Token_SEMICOLON extends PHP_Token -{ -} - -class PHP_Token_SL extends PHP_Token -{ -} - -class PHP_Token_SL_EQUAL extends PHP_Token -{ -} - -class PHP_Token_SR extends PHP_Token -{ -} - -class PHP_Token_SR_EQUAL extends PHP_Token -{ -} - -class PHP_Token_START_HEREDOC extends PHP_Token -{ -} - -class PHP_Token_STATIC extends PHP_Token -{ -} - -class PHP_Token_STRING extends PHP_Token -{ -} - -class PHP_Token_STRING_CAST extends PHP_Token -{ -} - -class PHP_Token_STRING_VARNAME extends PHP_Token -{ -} - -class PHP_Token_SWITCH extends PHP_Token -{ -} - -class PHP_Token_THROW extends PHP_Token -{ -} - -class PHP_Token_TILDE extends PHP_Token -{ -} - -class PHP_Token_TRY extends PHP_Token -{ -} - -class PHP_Token_UNSET extends PHP_Token -{ -} - -class PHP_Token_UNSET_CAST extends PHP_Token -{ -} - -class PHP_Token_USE extends PHP_Token -{ -} - -class PHP_Token_USE_FUNCTION extends PHP_Token -{ -} - -class PHP_Token_VAR extends PHP_Token -{ -} - -class PHP_Token_VARIABLE extends PHP_Token -{ -} - -class PHP_Token_WHILE extends PHP_Token -{ -} - -class PHP_Token_WHITESPACE extends PHP_Token -{ -} - -class PHP_Token_XOR_EQUAL extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.1 -class PHP_Token_HALT_COMPILER extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.3 -class PHP_Token_DIR extends PHP_Token -{ -} - -class PHP_Token_GOTO extends PHP_Token -{ -} - -class PHP_Token_NAMESPACE extends PHP_TokenWithScope -{ - /** - * @return string - */ - public function getName() - { - $tokens = $this->tokenStream->tokens(); - $namespace = (string) $tokens[$this->id + 2]; - - for ($i = $this->id + 3;; $i += 2) { - if (isset($tokens[$i]) && - $tokens[$i] instanceof PHP_Token_NS_SEPARATOR) { - $namespace .= '\\' . $tokens[$i + 1]; - } else { - break; - } - } - - return $namespace; - } -} - -class PHP_Token_NS_C extends PHP_Token -{ -} - -class PHP_Token_NS_SEPARATOR extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.4 -class PHP_Token_CALLABLE extends PHP_Token -{ -} - -class PHP_Token_INSTEADOF extends PHP_Token -{ -} - -class PHP_Token_TRAIT extends PHP_Token_INTERFACE -{ -} - -class PHP_Token_TRAIT_C extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.5 -class PHP_Token_FINALLY extends PHP_Token -{ -} - -class PHP_Token_YIELD extends PHP_Token -{ -} - -// Tokens introduced in PHP 5.6 -class PHP_Token_ELLIPSIS extends PHP_Token -{ -} - -class PHP_Token_POW extends PHP_Token -{ -} - -class PHP_Token_POW_EQUAL extends PHP_Token -{ -} - -// Tokens introduced in PHP 7.0 -class PHP_Token_COALESCE extends PHP_Token -{ -} - -class PHP_Token_SPACESHIP extends PHP_Token -{ -} - -class PHP_Token_YIELD_FROM extends PHP_Token -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * A caching factory for token stream objects. - */ -class PHP_Token_Stream_CachingFactory -{ - /** - * @var array - */ - protected static $cache = []; - - /** - * @param string $filename - * - * @return PHP_Token_Stream - */ - public static function get($filename) - { - if (!isset(self::$cache[$filename])) { - self::$cache[$filename] = new PHP_Token_Stream($filename); - } - - return self::$cache[$filename]; - } - - /** - * @param string $filename - */ - public static function clear($filename = null) - { - if (is_string($filename)) { - unset(self::$cache[$filename]); - } else { - self::$cache = []; - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * A stream of PHP tokens. - */ -class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator -{ - /** - * @var array - */ - protected static $customTokens = [ - '(' => 'PHP_Token_OPEN_BRACKET', - ')' => 'PHP_Token_CLOSE_BRACKET', - '[' => 'PHP_Token_OPEN_SQUARE', - ']' => 'PHP_Token_CLOSE_SQUARE', - '{' => 'PHP_Token_OPEN_CURLY', - '}' => 'PHP_Token_CLOSE_CURLY', - ';' => 'PHP_Token_SEMICOLON', - '.' => 'PHP_Token_DOT', - ',' => 'PHP_Token_COMMA', - '=' => 'PHP_Token_EQUAL', - '<' => 'PHP_Token_LT', - '>' => 'PHP_Token_GT', - '+' => 'PHP_Token_PLUS', - '-' => 'PHP_Token_MINUS', - '*' => 'PHP_Token_MULT', - '/' => 'PHP_Token_DIV', - '?' => 'PHP_Token_QUESTION_MARK', - '!' => 'PHP_Token_EXCLAMATION_MARK', - ':' => 'PHP_Token_COLON', - '"' => 'PHP_Token_DOUBLE_QUOTES', - '@' => 'PHP_Token_AT', - '&' => 'PHP_Token_AMPERSAND', - '%' => 'PHP_Token_PERCENT', - '|' => 'PHP_Token_PIPE', - '$' => 'PHP_Token_DOLLAR', - '^' => 'PHP_Token_CARET', - '~' => 'PHP_Token_TILDE', - '`' => 'PHP_Token_BACKTICK' - ]; - - /** - * @var string - */ - protected $filename; - - /** - * @var array - */ - protected $tokens = []; - - /** - * @var int - */ - protected $position = 0; - - /** - * @var array - */ - protected $linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; - - /** - * @var array - */ - protected $classes; - - /** - * @var array - */ - protected $functions; - - /** - * @var array - */ - protected $includes; - - /** - * @var array - */ - protected $interfaces; - - /** - * @var array - */ - protected $traits; - - /** - * @var array - */ - protected $lineToFunctionMap = []; - - /** - * Constructor. - * - * @param string $sourceCode - */ - public function __construct($sourceCode) - { - if (is_file($sourceCode)) { - $this->filename = $sourceCode; - $sourceCode = file_get_contents($sourceCode); - } - - $this->scan($sourceCode); - } - - /** - * Destructor. - */ - public function __destruct() - { - $this->tokens = []; - } - - /** - * @return string - */ - public function __toString() - { - $buffer = ''; - - foreach ($this as $token) { - $buffer .= $token; - } - - return $buffer; - } - - /** - * @return string - */ - public function getFilename() - { - return $this->filename; - } - - /** - * Scans the source for sequences of characters and converts them into a - * stream of tokens. - * - * @param string $sourceCode - */ - protected function scan($sourceCode) - { - $id = 0; - $line = 1; - $tokens = token_get_all($sourceCode); - $numTokens = count($tokens); - - $lastNonWhitespaceTokenWasDoubleColon = false; - - for ($i = 0; $i < $numTokens; ++$i) { - $token = $tokens[$i]; - $skip = 0; - - if (is_array($token)) { - $name = substr(token_name($token[0]), 2); - $text = $token[1]; - - if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') { - $name = 'CLASS_NAME_CONSTANT'; - } elseif ($name == 'USE' && isset($tokens[$i + 2][0]) && $tokens[$i + 2][0] == T_FUNCTION) { - $name = 'USE_FUNCTION'; - $text .= $tokens[$i + 1][1] . $tokens[$i + 2][1]; - $skip = 2; - } - - $tokenClass = 'PHP_Token_' . $name; - } else { - $text = $token; - $tokenClass = self::$customTokens[$token]; - } - - $this->tokens[] = new $tokenClass($text, $line, $this, $id++); - $lines = substr_count($text, "\n"); - $line += $lines; - - if ($tokenClass == 'PHP_Token_HALT_COMPILER') { - break; - } elseif ($tokenClass == 'PHP_Token_COMMENT' || - $tokenClass == 'PHP_Token_DOC_COMMENT') { - $this->linesOfCode['cloc'] += $lines + 1; - } - - if ($name == 'DOUBLE_COLON') { - $lastNonWhitespaceTokenWasDoubleColon = true; - } elseif ($name != 'WHITESPACE') { - $lastNonWhitespaceTokenWasDoubleColon = false; - } - - $i += $skip; - } - - $this->linesOfCode['loc'] = substr_count($sourceCode, "\n"); - $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] - - $this->linesOfCode['cloc']; - } - - /** - * @return int - */ - public function count() - { - return count($this->tokens); - } - - /** - * @return PHP_Token[] - */ - public function tokens() - { - return $this->tokens; - } - - /** - * @return array - */ - public function getClasses() - { - if ($this->classes !== null) { - return $this->classes; - } - - $this->parse(); - - return $this->classes; - } - - /** - * @return array - */ - public function getFunctions() - { - if ($this->functions !== null) { - return $this->functions; - } - - $this->parse(); - - return $this->functions; - } - - /** - * @return array - */ - public function getInterfaces() - { - if ($this->interfaces !== null) { - return $this->interfaces; - } - - $this->parse(); - - return $this->interfaces; - } - - /** - * @return array - */ - public function getTraits() - { - if ($this->traits !== null) { - return $this->traits; - } - - $this->parse(); - - return $this->traits; - } - - /** - * Gets the names of all files that have been included - * using include(), include_once(), require() or require_once(). - * - * Parameter $categorize set to TRUE causing this function to return a - * multi-dimensional array with categories in the keys of the first dimension - * and constants and their values in the second dimension. - * - * Parameter $category allow to filter following specific inclusion type - * - * @param bool $categorize OPTIONAL - * @param string $category OPTIONAL Either 'require_once', 'require', - * 'include_once', 'include'. - * - * @return array - */ - public function getIncludes($categorize = false, $category = null) - { - if ($this->includes === null) { - $this->includes = [ - 'require_once' => [], - 'require' => [], - 'include_once' => [], - 'include' => [] - ]; - - foreach ($this->tokens as $token) { - switch (get_class($token)) { - case 'PHP_Token_REQUIRE_ONCE': - case 'PHP_Token_REQUIRE': - case 'PHP_Token_INCLUDE_ONCE': - case 'PHP_Token_INCLUDE': - $this->includes[$token->getType()][] = $token->getName(); - break; - } - } - } - - if (isset($this->includes[$category])) { - $includes = $this->includes[$category]; - } elseif ($categorize === false) { - $includes = array_merge( - $this->includes['require_once'], - $this->includes['require'], - $this->includes['include_once'], - $this->includes['include'] - ); - } else { - $includes = $this->includes; - } - - return $includes; - } - - /** - * Returns the name of the function or method a line belongs to. - * - * @return string or null if the line is not in a function or method - */ - public function getFunctionForLine($line) - { - $this->parse(); - - if (isset($this->lineToFunctionMap[$line])) { - return $this->lineToFunctionMap[$line]; - } - } - - protected function parse() - { - $this->interfaces = []; - $this->classes = []; - $this->traits = []; - $this->functions = []; - $class = []; - $classEndLine = []; - $trait = false; - $traitEndLine = false; - $interface = false; - $interfaceEndLine = false; - - foreach ($this->tokens as $token) { - switch (get_class($token)) { - case 'PHP_Token_HALT_COMPILER': - return; - - case 'PHP_Token_INTERFACE': - $interface = $token->getName(); - $interfaceEndLine = $token->getEndLine(); - - $this->interfaces[$interface] = [ - 'methods' => [], - 'parent' => $token->getParent(), - 'keywords' => $token->getKeywords(), - 'docblock' => $token->getDocblock(), - 'startLine' => $token->getLine(), - 'endLine' => $interfaceEndLine, - 'package' => $token->getPackage(), - 'file' => $this->filename - ]; - break; - - case 'PHP_Token_CLASS': - case 'PHP_Token_TRAIT': - $tmp = [ - 'methods' => [], - 'parent' => $token->getParent(), - 'interfaces'=> $token->getInterfaces(), - 'keywords' => $token->getKeywords(), - 'docblock' => $token->getDocblock(), - 'startLine' => $token->getLine(), - 'endLine' => $token->getEndLine(), - 'package' => $token->getPackage(), - 'file' => $this->filename - ]; - - if ($token instanceof PHP_Token_CLASS) { - $class[] = $token->getName(); - $classEndLine[] = $token->getEndLine(); - - $this->classes[$class[count($class) - 1]] = $tmp; - } else { - $trait = $token->getName(); - $traitEndLine = $token->getEndLine(); - $this->traits[$trait] = $tmp; - } - break; - - case 'PHP_Token_FUNCTION': - $name = $token->getName(); - $tmp = [ - 'docblock' => $token->getDocblock(), - 'keywords' => $token->getKeywords(), - 'visibility'=> $token->getVisibility(), - 'signature' => $token->getSignature(), - 'startLine' => $token->getLine(), - 'endLine' => $token->getEndLine(), - 'ccn' => $token->getCCN(), - 'file' => $this->filename - ]; - - if (empty($class) && - $trait === false && - $interface === false) { - $this->functions[$name] = $tmp; - - $this->addFunctionToMap( - $name, - $tmp['startLine'], - $tmp['endLine'] - ); - } elseif (!empty($class)) { - $this->classes[$class[count($class) - 1]]['methods'][$name] = $tmp; - - $this->addFunctionToMap( - $class[count($class) - 1] . '::' . $name, - $tmp['startLine'], - $tmp['endLine'] - ); - } elseif ($trait !== false) { - $this->traits[$trait]['methods'][$name] = $tmp; - - $this->addFunctionToMap( - $trait . '::' . $name, - $tmp['startLine'], - $tmp['endLine'] - ); - } else { - $this->interfaces[$interface]['methods'][$name] = $tmp; - } - break; - - case 'PHP_Token_CLOSE_CURLY': - if (!empty($classEndLine) && - $classEndLine[count($classEndLine) - 1] == $token->getLine()) { - array_pop($classEndLine); - array_pop($class); - } elseif ($traitEndLine !== false && - $traitEndLine == $token->getLine()) { - $trait = false; - $traitEndLine = false; - } elseif ($interfaceEndLine !== false && - $interfaceEndLine == $token->getLine()) { - $interface = false; - $interfaceEndLine = false; - } - break; - } - } - } - - /** - * @return array - */ - public function getLinesOfCode() - { - return $this->linesOfCode; - } - - /** - */ - public function rewind() - { - $this->position = 0; - } - - /** - * @return bool - */ - public function valid() - { - return isset($this->tokens[$this->position]); - } - - /** - * @return int - */ - public function key() - { - return $this->position; - } - - /** - * @return PHP_Token - */ - public function current() - { - return $this->tokens[$this->position]; - } - - /** - */ - public function next() - { - $this->position++; - } - - /** - * @param int $offset - * - * @return bool - */ - public function offsetExists($offset) - { - return isset($this->tokens[$offset]); - } - - /** - * @param int $offset - * - * @return mixed - * - * @throws OutOfBoundsException - */ - public function offsetGet($offset) - { - if (!$this->offsetExists($offset)) { - throw new OutOfBoundsException( - sprintf( - 'No token at position "%s"', - $offset - ) - ); - } - - return $this->tokens[$offset]; - } - - /** - * @param int $offset - * @param mixed $value - */ - public function offsetSet($offset, $value) - { - $this->tokens[$offset] = $value; - } - - /** - * @param int $offset - * - * @throws OutOfBoundsException - */ - public function offsetUnset($offset) - { - if (!$this->offsetExists($offset)) { - throw new OutOfBoundsException( - sprintf( - 'No token at position "%s"', - $offset - ) - ); - } - - unset($this->tokens[$offset]); - } - - /** - * Seek to an absolute position. - * - * @param int $position - * - * @throws OutOfBoundsException - */ - public function seek($position) - { - $this->position = $position; - - if (!$this->valid()) { - throw new OutOfBoundsException( - sprintf( - 'No token at position "%s"', - $this->position - ) - ); - } - } - - /** - * @param string $name - * @param int $startLine - * @param int $endLine - */ - private function addFunctionToMap($name, $startLine, $endLine) - { - for ($line = $startLine; $line <= $endLine; $line++) { - $this->lineToFunctionMap[$line] = $name; - } - } -} -php-token-stream - -Copyright (c) 2009-2018, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - - - - This Schema file defines the rules by which the XML configuration file of PHPUnit 7.5 may be structured. - - - - - - Root Element - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The main type specifying the document structure - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Webmozart\Assert; - -use ArrayAccess; -use BadMethodCallException; -use Closure; -use Countable; -use Exception; -use InvalidArgumentException; -use Throwable; -use Traversable; - -/** - * Efficient assertions to validate the input/output of your methods. - * - * @method static void nullOrString($value, $message = '') - * @method static void nullOrStringNotEmpty($value, $message = '') - * @method static void nullOrInteger($value, $message = '') - * @method static void nullOrIntegerish($value, $message = '') - * @method static void nullOrFloat($value, $message = '') - * @method static void nullOrNumeric($value, $message = '') - * @method static void nullOrNatural($value, $message = '') - * @method static void nullOrBoolean($value, $message = '') - * @method static void nullOrScalar($value, $message = '') - * @method static void nullOrObject($value, $message = '') - * @method static void nullOrResource($value, $type = null, $message = '') - * @method static void nullOrIsCallable($value, $message = '') - * @method static void nullOrIsArray($value, $message = '') - * @method static void nullOrIsTraversable($value, $message = '') - * @method static void nullOrIsArrayAccessible($value, $message = '') - * @method static void nullOrIsCountable($value, $message = '') - * @method static void nullOrIsIterable($value, $message = '') - * @method static void nullOrIsInstanceOf($value, $class, $message = '') - * @method static void nullOrNotInstanceOf($value, $class, $message = '') - * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '') - * @method static void nullOrIsEmpty($value, $message = '') - * @method static void nullOrNotEmpty($value, $message = '') - * @method static void nullOrTrue($value, $message = '') - * @method static void nullOrFalse($value, $message = '') - * @method static void nullOrIp($value, $message = '') - * @method static void nullOrIpv4($value, $message = '') - * @method static void nullOrIpv6($value, $message = '') - * @method static void nullOrEq($value, $value2, $message = '') - * @method static void nullOrNotEq($value,$value2, $message = '') - * @method static void nullOrSame($value, $value2, $message = '') - * @method static void nullOrNotSame($value, $value2, $message = '') - * @method static void nullOrGreaterThan($value, $value2, $message = '') - * @method static void nullOrGreaterThanEq($value, $value2, $message = '') - * @method static void nullOrLessThan($value, $value2, $message = '') - * @method static void nullOrLessThanEq($value, $value2, $message = '') - * @method static void nullOrRange($value, $min, $max, $message = '') - * @method static void nullOrOneOf($value, $values, $message = '') - * @method static void nullOrContains($value, $subString, $message = '') - * @method static void nullOrNotContains($value, $subString, $message = '') - * @method static void nullOrNotWhitespaceOnly($value, $message = '') - * @method static void nullOrStartsWith($value, $prefix, $message = '') - * @method static void nullOrStartsWithLetter($value, $message = '') - * @method static void nullOrEndsWith($value, $suffix, $message = '') - * @method static void nullOrRegex($value, $pattern, $message = '') - * @method static void nullOrNotRegex($value, $pattern, $message = '') - * @method static void nullOrAlpha($value, $message = '') - * @method static void nullOrDigits($value, $message = '') - * @method static void nullOrAlnum($value, $message = '') - * @method static void nullOrLower($value, $message = '') - * @method static void nullOrUpper($value, $message = '') - * @method static void nullOrLength($value, $length, $message = '') - * @method static void nullOrMinLength($value, $min, $message = '') - * @method static void nullOrMaxLength($value, $max, $message = '') - * @method static void nullOrLengthBetween($value, $min, $max, $message = '') - * @method static void nullOrFileExists($value, $message = '') - * @method static void nullOrFile($value, $message = '') - * @method static void nullOrDirectory($value, $message = '') - * @method static void nullOrReadable($value, $message = '') - * @method static void nullOrWritable($value, $message = '') - * @method static void nullOrClassExists($value, $message = '') - * @method static void nullOrSubclassOf($value, $class, $message = '') - * @method static void nullOrInterfaceExists($value, $message = '') - * @method static void nullOrImplementsInterface($value, $interface, $message = '') - * @method static void nullOrPropertyExists($value, $property, $message = '') - * @method static void nullOrPropertyNotExists($value, $property, $message = '') - * @method static void nullOrMethodExists($value, $method, $message = '') - * @method static void nullOrMethodNotExists($value, $method, $message = '') - * @method static void nullOrKeyExists($value, $key, $message = '') - * @method static void nullOrKeyNotExists($value, $key, $message = '') - * @method static void nullOrCount($value, $key, $message = '') - * @method static void nullOrMinCount($value, $min, $message = '') - * @method static void nullOrMaxCount($value, $max, $message = '') - * @method static void nullOrIsList($value, $message = '') - * @method static void nullOrIsMap($value, $message = '') - * @method static void nullOrCountBetween($value, $min, $max, $message = '') - * @method static void nullOrUuid($values, $message = '') - * @method static void nullOrThrows($expression, $class = 'Exception', $message = '') - * @method static void allString($values, $message = '') - * @method static void allStringNotEmpty($values, $message = '') - * @method static void allInteger($values, $message = '') - * @method static void allIntegerish($values, $message = '') - * @method static void allFloat($values, $message = '') - * @method static void allNumeric($values, $message = '') - * @method static void allNatural($values, $message = '') - * @method static void allBoolean($values, $message = '') - * @method static void allScalar($values, $message = '') - * @method static void allObject($values, $message = '') - * @method static void allResource($values, $type = null, $message = '') - * @method static void allIsCallable($values, $message = '') - * @method static void allIsArray($values, $message = '') - * @method static void allIsTraversable($values, $message = '') - * @method static void allIsArrayAccessible($values, $message = '') - * @method static void allIsCountable($values, $message = '') - * @method static void allIsIterable($values, $message = '') - * @method static void allIsInstanceOf($values, $class, $message = '') - * @method static void allNotInstanceOf($values, $class, $message = '') - * @method static void allIsInstanceOfAny($values, $classes, $message = '') - * @method static void allNull($values, $message = '') - * @method static void allNotNull($values, $message = '') - * @method static void allIsEmpty($values, $message = '') - * @method static void allNotEmpty($values, $message = '') - * @method static void allTrue($values, $message = '') - * @method static void allFalse($values, $message = '') - * @method static void allIp($values, $message = '') - * @method static void allIpv4($values, $message = '') - * @method static void allIpv6($values, $message = '') - * @method static void allEq($values, $value2, $message = '') - * @method static void allNotEq($values,$value2, $message = '') - * @method static void allSame($values, $value2, $message = '') - * @method static void allNotSame($values, $value2, $message = '') - * @method static void allGreaterThan($values, $value2, $message = '') - * @method static void allGreaterThanEq($values, $value2, $message = '') - * @method static void allLessThan($values, $value2, $message = '') - * @method static void allLessThanEq($values, $value2, $message = '') - * @method static void allRange($values, $min, $max, $message = '') - * @method static void allOneOf($values, $values, $message = '') - * @method static void allContains($values, $subString, $message = '') - * @method static void allNotContains($values, $subString, $message = '') - * @method static void allNotWhitespaceOnly($values, $message = '') - * @method static void allStartsWith($values, $prefix, $message = '') - * @method static void allStartsWithLetter($values, $message = '') - * @method static void allEndsWith($values, $suffix, $message = '') - * @method static void allRegex($values, $pattern, $message = '') - * @method static void allNotRegex($values, $pattern, $message = '') - * @method static void allAlpha($values, $message = '') - * @method static void allDigits($values, $message = '') - * @method static void allAlnum($values, $message = '') - * @method static void allLower($values, $message = '') - * @method static void allUpper($values, $message = '') - * @method static void allLength($values, $length, $message = '') - * @method static void allMinLength($values, $min, $message = '') - * @method static void allMaxLength($values, $max, $message = '') - * @method static void allLengthBetween($values, $min, $max, $message = '') - * @method static void allFileExists($values, $message = '') - * @method static void allFile($values, $message = '') - * @method static void allDirectory($values, $message = '') - * @method static void allReadable($values, $message = '') - * @method static void allWritable($values, $message = '') - * @method static void allClassExists($values, $message = '') - * @method static void allSubclassOf($values, $class, $message = '') - * @method static void allInterfaceExists($values, $message = '') - * @method static void allImplementsInterface($values, $interface, $message = '') - * @method static void allPropertyExists($values, $property, $message = '') - * @method static void allPropertyNotExists($values, $property, $message = '') - * @method static void allMethodExists($values, $method, $message = '') - * @method static void allMethodNotExists($values, $method, $message = '') - * @method static void allKeyExists($values, $key, $message = '') - * @method static void allKeyNotExists($values, $key, $message = '') - * @method static void allCount($values, $key, $message = '') - * @method static void allMinCount($values, $min, $message = '') - * @method static void allMaxCount($values, $max, $message = '') - * @method static void allCountBetween($values, $min, $max, $message = '') - * @method static void allIsList($values, $message = '') - * @method static void allIsMap($values, $message = '') - * @method static void allUuid($values, $message = '') - * @method static void allThrows($expressions, $class = 'Exception', $message = '') - * - * @since 1.0 - * - * @author Bernhard Schussek - */ -class Assert -{ - public static function string($value, $message = '') - { - if (!is_string($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a string. Got: %s', - static::typeToString($value) - )); - } - } - - public static function stringNotEmpty($value, $message = '') - { - static::string($value, $message); - static::notEq($value, '', $message); - } - - public static function integer($value, $message = '') - { - if (!is_int($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an integer. Got: %s', - static::typeToString($value) - )); - } - } - - public static function integerish($value, $message = '') - { - if (!is_numeric($value) || $value != (int) $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an integerish value. Got: %s', - static::typeToString($value) - )); - } - } - - public static function float($value, $message = '') - { - if (!is_float($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a float. Got: %s', - static::typeToString($value) - )); - } - } - - public static function numeric($value, $message = '') - { - if (!is_numeric($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a numeric. Got: %s', - static::typeToString($value) - )); - } - } - - public static function natural($value, $message = '') - { - if (!is_int($value) || $value < 0) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a non-negative integer. Got %s', - static::valueToString($value) - )); - } - } - - public static function boolean($value, $message = '') - { - if (!is_bool($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a boolean. Got: %s', - static::typeToString($value) - )); - } - } - - public static function scalar($value, $message = '') - { - if (!is_scalar($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a scalar. Got: %s', - static::typeToString($value) - )); - } - } - - public static function object($value, $message = '') - { - if (!is_object($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an object. Got: %s', - static::typeToString($value) - )); - } - } - - public static function resource($value, $type = null, $message = '') - { - if (!is_resource($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a resource. Got: %s', - static::typeToString($value) - )); - } - - if ($type && $type !== get_resource_type($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a resource of type %2$s. Got: %s', - static::typeToString($value), - $type - )); - } - } - - public static function isCallable($value, $message = '') - { - if (!is_callable($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a callable. Got: %s', - static::typeToString($value) - )); - } - } - - public static function isArray($value, $message = '') - { - if (!is_array($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an array. Got: %s', - static::typeToString($value) - )); - } - } - - public static function isTraversable($value, $message = '') - { - @trigger_error( - sprintf( - 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', - __METHOD__ - ), - E_USER_DEPRECATED - ); - - if (!is_array($value) && !($value instanceof Traversable)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a traversable. Got: %s', - static::typeToString($value) - )); - } - } - - public static function isArrayAccessible($value, $message = '') - { - if (!is_array($value) && !($value instanceof ArrayAccess)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an array accessible. Got: %s', - static::typeToString($value) - )); - } - } - - public static function isCountable($value, $message = '') - { - if (!is_array($value) && !($value instanceof Countable)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a countable. Got: %s', - static::typeToString($value) - )); - } - } - - public static function isIterable($value, $message = '') - { - if (!is_array($value) && !($value instanceof Traversable)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an iterable. Got: %s', - static::typeToString($value) - )); - } - } - - public static function isInstanceOf($value, $class, $message = '') - { - if (!($value instanceof $class)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an instance of %2$s. Got: %s', - static::typeToString($value), - $class - )); - } - } - - public static function notInstanceOf($value, $class, $message = '') - { - if ($value instanceof $class) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an instance other than %2$s. Got: %s', - static::typeToString($value), - $class - )); - } - } - - public static function isInstanceOfAny($value, array $classes, $message = '') - { - foreach ($classes as $class) { - if ($value instanceof $class) { - return; - } - } - - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an instance of any of %2$s. Got: %s', - static::typeToString($value), - implode(', ', array_map(array('static', 'valueToString'), $classes)) - )); - } - - public static function isEmpty($value, $message = '') - { - if (!empty($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an empty value. Got: %s', - static::valueToString($value) - )); - } - } - - public static function notEmpty($value, $message = '') - { - if (empty($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a non-empty value. Got: %s', - static::valueToString($value) - )); - } - } - - public static function null($value, $message = '') - { - if (null !== $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected null. Got: %s', - static::valueToString($value) - )); - } - } - - public static function notNull($value, $message = '') - { - if (null === $value) { - static::reportInvalidArgument( - $message ?: 'Expected a value other than null.' - ); - } - } - - public static function true($value, $message = '') - { - if (true !== $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to be true. Got: %s', - static::valueToString($value) - )); - } - } - - public static function false($value, $message = '') - { - if (false !== $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to be false. Got: %s', - static::valueToString($value) - )); - } - } - - public static function ip($value, $message = '') - { - if (false === filter_var($value, FILTER_VALIDATE_IP)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to be an IP. Got: %s', - static::valueToString($value) - )); - } - } - - public static function ipv4($value, $message = '') - { - if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to be an IPv4. Got: %s', - static::valueToString($value) - )); - } - } - - public static function ipv6($value, $message = '') - { - if (false === filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to be an IPv6. Got %s', - static::valueToString($value) - )); - } - } - - public static function eq($value, $value2, $message = '') - { - if ($value2 != $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value equal to %2$s. Got: %s', - static::valueToString($value), - static::valueToString($value2) - )); - } - } - - public static function notEq($value, $value2, $message = '') - { - if ($value2 == $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a different value than %s.', - static::valueToString($value2) - )); - } - } - - public static function same($value, $value2, $message = '') - { - if ($value2 !== $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value identical to %2$s. Got: %s', - static::valueToString($value), - static::valueToString($value2) - )); - } - } - - public static function notSame($value, $value2, $message = '') - { - if ($value2 === $value) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value not identical to %s.', - static::valueToString($value2) - )); - } - } - - public static function greaterThan($value, $limit, $message = '') - { - if ($value <= $limit) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value greater than %2$s. Got: %s', - static::valueToString($value), - static::valueToString($limit) - )); - } - } - - public static function greaterThanEq($value, $limit, $message = '') - { - if ($value < $limit) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value greater than or equal to %2$s. Got: %s', - static::valueToString($value), - static::valueToString($limit) - )); - } - } - - public static function lessThan($value, $limit, $message = '') - { - if ($value >= $limit) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value less than %2$s. Got: %s', - static::valueToString($value), - static::valueToString($limit) - )); - } - } - - public static function lessThanEq($value, $limit, $message = '') - { - if ($value > $limit) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value less than or equal to %2$s. Got: %s', - static::valueToString($value), - static::valueToString($limit) - )); - } - } - - public static function range($value, $min, $max, $message = '') - { - if ($value < $min || $value > $max) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value between %2$s and %3$s. Got: %s', - static::valueToString($value), - static::valueToString($min), - static::valueToString($max) - )); - } - } - - public static function oneOf($value, array $values, $message = '') - { - if (!in_array($value, $values, true)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected one of: %2$s. Got: %s', - static::valueToString($value), - implode(', ', array_map(array('static', 'valueToString'), $values)) - )); - } - } - - public static function contains($value, $subString, $message = '') - { - if (false === strpos($value, $subString)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain %2$s. Got: %s', - static::valueToString($value), - static::valueToString($subString) - )); - } - } - - public static function notContains($value, $subString, $message = '') - { - if (false !== strpos($value, $subString)) { - static::reportInvalidArgument(sprintf( - $message ?: '%2$s was not expected to be contained in a value. Got: %s', - static::valueToString($value), - static::valueToString($subString) - )); - } - } - - public static function notWhitespaceOnly($value, $message = '') - { - if (preg_match('/^\s*$/', $value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a non-whitespace string. Got: %s', - static::valueToString($value) - )); - } - } - - public static function startsWith($value, $prefix, $message = '') - { - if (0 !== strpos($value, $prefix)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to start with %2$s. Got: %s', - static::valueToString($value), - static::valueToString($prefix) - )); - } - } - - public static function startsWithLetter($value, $message = '') - { - $valid = isset($value[0]); - - if ($valid) { - $locale = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $valid = ctype_alpha($value[0]); - setlocale(LC_CTYPE, $locale); - } - - if (!$valid) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to start with a letter. Got: %s', - static::valueToString($value) - )); - } - } - - public static function endsWith($value, $suffix, $message = '') - { - if ($suffix !== substr($value, -static::strlen($suffix))) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to end with %2$s. Got: %s', - static::valueToString($value), - static::valueToString($suffix) - )); - } - } - - public static function regex($value, $pattern, $message = '') - { - if (!preg_match($pattern, $value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The value %s does not match the expected pattern.', - static::valueToString($value) - )); - } - } - - public static function notRegex($value, $pattern, $message = '') - { - if (preg_match($pattern, $value, $matches, PREG_OFFSET_CAPTURE)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The value %s matches the pattern %s (at offset %d).', - static::valueToString($value), - static::valueToString($pattern), - $matches[0][1] - )); - } - } - - public static function alpha($value, $message = '') - { - $locale = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $valid = !ctype_alpha($value); - setlocale(LC_CTYPE, $locale); - - if ($valid) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain only letters. Got: %s', - static::valueToString($value) - )); - } - } - - public static function digits($value, $message = '') - { - $locale = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $valid = !ctype_digit($value); - setlocale(LC_CTYPE, $locale); - - if ($valid) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain digits only. Got: %s', - static::valueToString($value) - )); - } - } - - public static function alnum($value, $message = '') - { - $locale = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $valid = !ctype_alnum($value); - setlocale(LC_CTYPE, $locale); - - if ($valid) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain letters and digits only. Got: %s', - static::valueToString($value) - )); - } - } - - public static function lower($value, $message = '') - { - $locale = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $valid = !ctype_lower($value); - setlocale(LC_CTYPE, $locale); - - if ($valid) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain lowercase characters only. Got: %s', - static::valueToString($value) - )); - } - } - - public static function upper($value, $message = '') - { - $locale = setlocale(LC_CTYPE, 0); - setlocale(LC_CTYPE, 'C'); - $valid = !ctype_upper($value); - setlocale(LC_CTYPE, $locale); - - if ($valid) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain uppercase characters only. Got: %s', - static::valueToString($value) - )); - } - } - - public static function length($value, $length, $message = '') - { - if ($length !== static::strlen($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain %2$s characters. Got: %s', - static::valueToString($value), - $length - )); - } - } - - public static function minLength($value, $min, $message = '') - { - if (static::strlen($value) < $min) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain at least %2$s characters. Got: %s', - static::valueToString($value), - $min - )); - } - } - - public static function maxLength($value, $max, $message = '') - { - if (static::strlen($value) > $max) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain at most %2$s characters. Got: %s', - static::valueToString($value), - $max - )); - } - } - - public static function lengthBetween($value, $min, $max, $message = '') - { - $length = static::strlen($value); - - if ($length < $min || $length > $max) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', - static::valueToString($value), - $min, - $max - )); - } - } - - public static function fileExists($value, $message = '') - { - static::string($value); - - if (!file_exists($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The file %s does not exist.', - static::valueToString($value) - )); - } - } - - public static function file($value, $message = '') - { - static::fileExists($value, $message); - - if (!is_file($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The path %s is not a file.', - static::valueToString($value) - )); - } - } - - public static function directory($value, $message = '') - { - static::fileExists($value, $message); - - if (!is_dir($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The path %s is no directory.', - static::valueToString($value) - )); - } - } - - public static function readable($value, $message = '') - { - if (!is_readable($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The path %s is not readable.', - static::valueToString($value) - )); - } - } - - public static function writable($value, $message = '') - { - if (!is_writable($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'The path %s is not writable.', - static::valueToString($value) - )); - } - } - - public static function classExists($value, $message = '') - { - if (!class_exists($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an existing class name. Got: %s', - static::valueToString($value) - )); - } - } - - public static function subclassOf($value, $class, $message = '') - { - if (!is_subclass_of($value, $class)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected a sub-class of %2$s. Got: %s', - static::valueToString($value), - static::valueToString($class) - )); - } - } - - public static function interfaceExists($value, $message = '') - { - if (!interface_exists($value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an existing interface name. got %s', - static::valueToString($value) - )); - } - } - - public static function implementsInterface($value, $interface, $message = '') - { - if (!in_array($interface, class_implements($value))) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an implementation of %2$s. Got: %s', - static::valueToString($value), - static::valueToString($interface) - )); - } - } - - public static function propertyExists($classOrObject, $property, $message = '') - { - if (!property_exists($classOrObject, $property)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected the property %s to exist.', - static::valueToString($property) - )); - } - } - - public static function propertyNotExists($classOrObject, $property, $message = '') - { - if (property_exists($classOrObject, $property)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected the property %s to not exist.', - static::valueToString($property) - )); - } - } - - public static function methodExists($classOrObject, $method, $message = '') - { - if (!method_exists($classOrObject, $method)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected the method %s to exist.', - static::valueToString($method) - )); - } - } - - public static function methodNotExists($classOrObject, $method, $message = '') - { - if (method_exists($classOrObject, $method)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected the method %s to not exist.', - static::valueToString($method) - )); - } - } - - public static function keyExists($array, $key, $message = '') - { - if (!(isset($array[$key]) || array_key_exists($key, $array))) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected the key %s to exist.', - static::valueToString($key) - )); - } - } - - public static function keyNotExists($array, $key, $message = '') - { - if (isset($array[$key]) || array_key_exists($key, $array)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected the key %s to not exist.', - static::valueToString($key) - )); - } - } - - public static function count($array, $number, $message = '') - { - static::eq( - count($array), - $number, - $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array)) - ); - } - - public static function minCount($array, $min, $message = '') - { - if (count($array) < $min) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an array to contain at least %2$d elements. Got: %d', - count($array), - $min - )); - } - } - - public static function maxCount($array, $max, $message = '') - { - if (count($array) > $max) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an array to contain at most %2$d elements. Got: %d', - count($array), - $max - )); - } - } - - public static function countBetween($array, $min, $max, $message = '') - { - $count = count($array); - - if ($count < $min || $count > $max) { - static::reportInvalidArgument(sprintf( - $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', - $count, - $min, - $max - )); - } - } - - public static function isList($array, $message = '') - { - if (!is_array($array) || !$array || array_keys($array) !== range(0, count($array) - 1)) { - static::reportInvalidArgument( - $message ?: 'Expected list - non-associative array.' - ); - } - } - - public static function isMap($array, $message = '') - { - if ( - !is_array($array) || - !$array || - array_keys($array) !== array_filter(array_keys($array), function ($key) { - return is_string($key); - }) - ) { - static::reportInvalidArgument( - $message ?: 'Expected map - associative array with string keys.' - ); - } - } - - public static function uuid($value, $message = '') - { - $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); - - // The nil UUID is special form of UUID that is specified to have all - // 128 bits set to zero. - if ('00000000-0000-0000-0000-000000000000' === $value) { - return; - } - - if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { - static::reportInvalidArgument(sprintf( - $message ?: 'Value %s is not a valid UUID.', - static::valueToString($value) - )); - } - } - - public static function throws(Closure $expression, $class = 'Exception', $message = '') - { - static::string($class); - - $actual = 'none'; - - try { - $expression(); - } catch (Exception $e) { - $actual = get_class($e); - if ($e instanceof $class) { - return; - } - } catch (Throwable $e) { - $actual = get_class($e); - if ($e instanceof $class) { - return; - } - } - - static::reportInvalidArgument($message ?: sprintf( - 'Expected to throw "%s", got "%s"', - $class, - $actual - )); - } - - public static function __callStatic($name, $arguments) - { - if ('nullOr' === substr($name, 0, 6)) { - if (null !== $arguments[0]) { - $method = lcfirst(substr($name, 6)); - call_user_func_array(array('static', $method), $arguments); - } - - return; - } - - if ('all' === substr($name, 0, 3)) { - static::isIterable($arguments[0]); - - $method = lcfirst(substr($name, 3)); - $args = $arguments; - - foreach ($arguments[0] as $entry) { - $args[0] = $entry; - - call_user_func_array(array('static', $method), $args); - } - - return; - } - - throw new BadMethodCallException('No such method: '.$name); - } - - protected static function valueToString($value) - { - if (null === $value) { - return 'null'; - } - - if (true === $value) { - return 'true'; - } - - if (false === $value) { - return 'false'; - } - - if (is_array($value)) { - return 'array'; - } - - if (is_object($value)) { - if (method_exists($value, '__toString')) { - return get_class($value).': '.self::valueToString($value->__toString()); - } - - return get_class($value); - } - - if (is_resource($value)) { - return 'resource'; - } - - if (is_string($value)) { - return '"'.$value.'"'; - } - - return (string) $value; - } - - protected static function typeToString($value) - { - return is_object($value) ? get_class($value) : gettype($value); - } - - protected static function strlen($value) - { - if (!function_exists('mb_detect_encoding')) { - return strlen($value); - } - - if (false === $encoding = mb_detect_encoding($value)) { - return strlen($value); - } - - return mb_strwidth($value, $encoding); - } - - protected static function reportInvalidArgument($message) - { - throw new InvalidArgumentException($message); - } - - private function __construct() - { - } -} -The MIT License (MIT) - -Copyright (c) 2014 Bernhard Schussek - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Environment; - -final class Console -{ - /** - * @var int - */ - public const STDIN = 0; - - /** - * @var int - */ - public const STDOUT = 1; - - /** - * @var int - */ - public const STDERR = 2; - - /** - * Returns true if STDOUT supports colorization. - * - * This code has been copied and adapted from - * Symfony\Component\Console\Output\StreamOutput. - */ - public function hasColorSupport(): bool - { - if ('Hyper' === \getenv('TERM_PROGRAM')) { - return true; - } - - if ($this->isWindows()) { - // @codeCoverageIgnoreStart - return (\defined('STDOUT') && \function_exists('sapi_windows_vt100_support') && @sapi_windows_vt100_support(\STDOUT)) - || false !== \getenv('ANSICON') - || 'ON' === \getenv('ConEmuANSI') - || 'xterm' === \getenv('TERM'); - // @codeCoverageIgnoreEnd - } - - if (!\defined('STDOUT')) { - // @codeCoverageIgnoreStart - return false; - // @codeCoverageIgnoreEnd - } - - if ($this->isInteractive(\STDOUT)) { - return true; - } - - $stat = @\fstat(\STDOUT); - // Check if formatted mode is S_IFCHR - return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; - } - - /** - * Returns the number of columns of the terminal. - * - * @codeCoverageIgnore - */ - public function getNumberOfColumns(): int - { - if ($this->isWindows()) { - return $this->getNumberOfColumnsWindows(); - } - - if (!$this->isInteractive(\defined('STDIN') ? \STDIN : self::STDIN)) { - return 80; - } - - return $this->getNumberOfColumnsInteractive(); - } - - /** - * Returns if the file descriptor is an interactive terminal or not. - * - * Normally, we want to use a resource as a parameter, yet sadly it's not always awailable, - * eg when running code in interactive console (`php -a`), STDIN/STDOUT/STDERR constants are not defined. - * - * @param int|resource $fileDescriptor - */ - public function isInteractive($fileDescriptor = self::STDOUT): bool - { - return (\is_resource($fileDescriptor) && \function_exists('stream_isatty') && @\stream_isatty($fileDescriptor)) // stream_isatty requires that descriptor is a real resource, not numeric ID of it - || (\function_exists('posix_isatty') && @\posix_isatty($fileDescriptor)); - } - - private function isWindows(): bool - { - return \DIRECTORY_SEPARATOR === '\\'; - } - - /** - * @codeCoverageIgnore - */ - private function getNumberOfColumnsInteractive(): int - { - if (\function_exists('shell_exec') && \preg_match('#\d+ (\d+)#', \shell_exec('stty size') ?? '', $match) === 1) { - if ((int) $match[1] > 0) { - return (int) $match[1]; - } - } - - if (\function_exists('shell_exec') && \preg_match('#columns = (\d+);#', \shell_exec('stty') ?? '', $match) === 1) { - if ((int) $match[1] > 0) { - return (int) $match[1]; - } - } - - return 80; - } - - /** - * @codeCoverageIgnore - */ - private function getNumberOfColumnsWindows(): int - { - $ansicon = \getenv('ANSICON'); - $columns = 80; - - if (\is_string($ansicon) && \preg_match('/^(\d+)x\d+ \(\d+x(\d+)\)$/', \trim($ansicon), $matches)) { - $columns = $matches[1]; - } elseif (\function_exists('proc_open')) { - $process = \proc_open( - 'mode CON', - [ - 1 => ['pipe', 'w'], - 2 => ['pipe', 'w'], - ], - $pipes, - null, - null, - ['suppress_errors' => true] - ); - - if (\is_resource($process)) { - $info = \stream_get_contents($pipes[1]); - - \fclose($pipes[1]); - \fclose($pipes[2]); - \proc_close($process); - - if (\preg_match('/--------+\r?\n.+?(\d+)\r?\n.+?(\d+)\r?\n/', $info, $matches)) { - $columns = $matches[2]; - } - } - } - - return $columns - 1; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Environment; - -/** - * Utility class for HHVM/PHP environment handling. - */ -final class Runtime -{ - /** - * @var string - */ - private static $binary; - - /** - * Returns true when Xdebug or PCOV is available or - * the runtime used is PHPDBG. - */ - public function canCollectCodeCoverage(): bool - { - return $this->hasXdebug() || $this->hasPCOV() || $this->hasPHPDBGCodeCoverage(); - } - - /** - * Returns true when Zend OPcache is loaded, enabled, and is configured to discard comments. - */ - public function discardsComments(): bool - { - if (!\extension_loaded('Zend OPcache')) { - return false; - } - - if (\ini_get('opcache.save_comments') !== '0') { - return false; - } - - if (\PHP_SAPI === 'cli' && \ini_get('opcache.enable_cli') === '1') { - return true; - } - - if (\PHP_SAPI !== 'cli' && \ini_get('opcache.enable') === '1') { - return true; - } - - return false; - } - - /** - * Returns the path to the binary of the current runtime. - * Appends ' --php' to the path when the runtime is HHVM. - */ - public function getBinary(): string - { - // HHVM - if (self::$binary === null && $this->isHHVM()) { - // @codeCoverageIgnoreStart - if ((self::$binary = \getenv('PHP_BINARY')) === false) { - self::$binary = \PHP_BINARY; - } - - self::$binary = \escapeshellarg(self::$binary) . ' --php' . - ' -d hhvm.php7.all=1'; - // @codeCoverageIgnoreEnd - } - - if (self::$binary === null && \PHP_BINARY !== '') { - self::$binary = \escapeshellarg(\PHP_BINARY); - } - - if (self::$binary === null) { - // @codeCoverageIgnoreStart - $possibleBinaryLocations = [ - \PHP_BINDIR . '/php', - \PHP_BINDIR . '/php-cli.exe', - \PHP_BINDIR . '/php.exe', - ]; - - foreach ($possibleBinaryLocations as $binary) { - if (\is_readable($binary)) { - self::$binary = \escapeshellarg($binary); - - break; - } - } - // @codeCoverageIgnoreEnd - } - - if (self::$binary === null) { - // @codeCoverageIgnoreStart - self::$binary = 'php'; - // @codeCoverageIgnoreEnd - } - - return self::$binary; - } - - public function getNameWithVersion(): string - { - return $this->getName() . ' ' . $this->getVersion(); - } - - public function getNameWithVersionAndCodeCoverageDriver(): string - { - if (!$this->canCollectCodeCoverage() || $this->hasPHPDBGCodeCoverage()) { - return $this->getNameWithVersion(); - } - - if ($this->hasXdebug()) { - return \sprintf( - '%s with Xdebug %s', - $this->getNameWithVersion(), - \phpversion('xdebug') - ); - } - - if ($this->hasPCOV()) { - return \sprintf( - '%s with PCOV %s', - $this->getNameWithVersion(), - \phpversion('pcov') - ); - } - } - - public function getName(): string - { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return 'HHVM'; - // @codeCoverageIgnoreEnd - } - - if ($this->isPHPDBG()) { - // @codeCoverageIgnoreStart - return 'PHPDBG'; - // @codeCoverageIgnoreEnd - } - - return 'PHP'; - } - - public function getVendorUrl(): string - { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return 'http://hhvm.com/'; - // @codeCoverageIgnoreEnd - } - - return 'https://secure.php.net/'; - } - - public function getVersion(): string - { - if ($this->isHHVM()) { - // @codeCoverageIgnoreStart - return HHVM_VERSION; - // @codeCoverageIgnoreEnd - } - - return \PHP_VERSION; - } - - /** - * Returns true when the runtime used is PHP and Xdebug is loaded. - */ - public function hasXdebug(): bool - { - return ($this->isPHP() || $this->isHHVM()) && \extension_loaded('xdebug'); - } - - /** - * Returns true when the runtime used is HHVM. - */ - public function isHHVM(): bool - { - return \defined('HHVM_VERSION'); - } - - /** - * Returns true when the runtime used is PHP without the PHPDBG SAPI. - */ - public function isPHP(): bool - { - return !$this->isHHVM() && !$this->isPHPDBG(); - } - - /** - * Returns true when the runtime used is PHP with the PHPDBG SAPI. - */ - public function isPHPDBG(): bool - { - return \PHP_SAPI === 'phpdbg' && !$this->isHHVM(); - } - - /** - * Returns true when the runtime used is PHP with the PHPDBG SAPI - * and the phpdbg_*_oplog() functions are available (PHP >= 7.0). - * - * @codeCoverageIgnore - */ - public function hasPHPDBGCodeCoverage(): bool - { - return $this->isPHPDBG(); - } - - /** - * Returns true when the runtime used is PHP with PCOV loaded and enabled - */ - public function hasPCOV(): bool - { - return $this->isPHP() && \extension_loaded('pcov') && \ini_get('pcov.enabled'); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\Environment; - -final class OperatingSystem -{ - /** - * Returns PHP_OS_FAMILY (if defined (which it is on PHP >= 7.2)). - * Returns a string (compatible with PHP_OS_FAMILY) derived from PHP_OS otherwise. - */ - public function getFamily(): string - { - if (\defined('PHP_OS_FAMILY')) { - return \PHP_OS_FAMILY; - } - - if (\DIRECTORY_SEPARATOR === '\\') { - return 'Windows'; - } - - switch (\PHP_OS) { - case 'Darwin': - return 'Darwin'; - - case 'DragonFly': - case 'FreeBSD': - case 'NetBSD': - case 'OpenBSD': - return 'BSD'; - - case 'Linux': - return 'Linux'; - - case 'SunOS': - return 'Solaris'; - - default: - return 'Unknown'; - } - } -} -sebastian/environment - -Copyright (c) 2014-2019, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\ObjectEnumerator; - -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\ObjectEnumerator; - -use SebastianBergmann\ObjectReflector\ObjectReflector; -use SebastianBergmann\RecursionContext\Context; - -/** - * Traverses array structures and object graphs - * to enumerate all referenced objects. - */ -class Enumerator -{ - /** - * Returns an array of all objects referenced either - * directly or indirectly by a variable. - * - * @param array|object $variable - * - * @return object[] - */ - public function enumerate($variable) - { - if (!is_array($variable) && !is_object($variable)) { - throw new InvalidArgumentException; - } - - if (isset(func_get_args()[1])) { - if (!func_get_args()[1] instanceof Context) { - throw new InvalidArgumentException; - } - - $processed = func_get_args()[1]; - } else { - $processed = new Context; - } - - $objects = []; - - if ($processed->contains($variable)) { - return $objects; - } - - $array = $variable; - $processed->add($variable); - - if (is_array($variable)) { - foreach ($array as $element) { - if (!is_array($element) && !is_object($element)) { - continue; - } - - $objects = array_merge( - $objects, - $this->enumerate($element, $processed) - ); - } - } else { - $objects[] = $variable; - - $reflector = new ObjectReflector; - - foreach ($reflector->getAttributes($variable) as $value) { - if (!is_array($value) && !is_object($value)) { - continue; - } - - $objects = array_merge( - $objects, - $this->enumerate($value, $processed) - ); - } - } - - return $objects; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\ObjectEnumerator; - -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann; - -/** - * @since Class available since Release 1.0.0 - */ -class Version -{ - /** - * @var string - */ - private $path; - - /** - * @var string - */ - private $release; - - /** - * @var string - */ - private $version; - - /** - * @param string $release - * @param string $path - */ - public function __construct($release, $path) - { - $this->release = $release; - $this->path = $path; - } - - /** - * @return string - */ - public function getVersion() - { - if ($this->version === null) { - if (count(explode('.', $this->release)) == 3) { - $this->version = $this->release; - } else { - $this->version = $this->release . '-dev'; - } - - $git = $this->getGitInformation($this->path); - - if ($git) { - if (count(explode('.', $this->release)) == 3) { - $this->version = $git; - } else { - $git = explode('-', $git); - - $this->version = $this->release . '-' . end($git); - } - } - } - - return $this->version; - } - - /** - * @param string $path - * - * @return bool|string - */ - private function getGitInformation($path) - { - if (!is_dir($path . DIRECTORY_SEPARATOR . '.git')) { - return false; - } - - $process = proc_open( - 'git describe --tags', - [ - 1 => ['pipe', 'w'], - 2 => ['pipe', 'w'], - ], - $pipes, - $path - ); - - if (!is_resource($process)) { - return false; - } - - $result = trim(stream_get_contents($pipes[1])); - - fclose($pipes[1]); - fclose($pipes[2]); - - $returnCode = proc_close($process); - - if ($returnCode !== 0) { - return false; - } - - return $result; - } -} -Version - -Copyright (c) 2013-2015, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -final class ConfigurationException extends InvalidArgumentException -{ - /** - * @param string $option - * @param string $expected - * @param mixed $value - * @param int $code - * @param null|\Exception $previous - */ - public function __construct( - string $option, - string $expected, - $value, - int $code = 0, - \Exception $previous = null - ) { - parent::__construct( - \sprintf( - 'Option "%s" must be %s, got "%s".', - $option, - $expected, - \is_object($value) ? \get_class($value) : (null === $value ? '' : \gettype($value) . '#' . $value) - ), - $code, - $previous - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -final class MemoryEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to): array - { - $cFrom = \count($from); - $cTo = \count($to); - - if ($cFrom === 0) { - return []; - } - - if ($cFrom === 1) { - if (\in_array($from[0], $to, true)) { - return [$from[0]]; - } - - return []; - } - - $i = (int) ($cFrom / 2); - $fromStart = \array_slice($from, 0, $i); - $fromEnd = \array_slice($from, $i); - $llB = $this->length($fromStart, $to); - $llE = $this->length(\array_reverse($fromEnd), \array_reverse($to)); - $jMax = 0; - $max = 0; - - for ($j = 0; $j <= $cTo; $j++) { - $m = $llB[$j] + $llE[$cTo - $j]; - - if ($m >= $max) { - $max = $m; - $jMax = $j; - } - } - - $toStart = \array_slice($to, 0, $jMax); - $toEnd = \array_slice($to, $jMax); - - return \array_merge( - $this->calculate($fromStart, $toStart), - $this->calculate($fromEnd, $toEnd) - ); - } - - private function length(array $from, array $to): array - { - $current = \array_fill(0, \count($to) + 1, 0); - $cFrom = \count($from); - $cTo = \count($to); - - for ($i = 0; $i < $cFrom; $i++) { - $prev = $current; - - for ($j = 0; $j < $cTo; $j++) { - if ($from[$i] === $to[$j]) { - $current[$j + 1] = $prev[$j] + 1; - } else { - $current[$j + 1] = \max($current[$j], $prev[$j + 1]); - } - } - } - - return $current; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCommonSubsequenceCalculator -{ - /** - * {@inheritdoc} - */ - public function calculate(array $from, array $to): array - { - $common = []; - $fromLength = \count($from); - $toLength = \count($to); - $width = $fromLength + 1; - $matrix = new \SplFixedArray($width * ($toLength + 1)); - - for ($i = 0; $i <= $fromLength; ++$i) { - $matrix[$i] = 0; - } - - for ($j = 0; $j <= $toLength; ++$j) { - $matrix[$j * $width] = 0; - } - - for ($i = 1; $i <= $fromLength; ++$i) { - for ($j = 1; $j <= $toLength; ++$j) { - $o = ($j * $width) + $i; - $matrix[$o] = \max( - $matrix[$o - 1], - $matrix[$o - $width], - $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 - ); - } - } - - $i = $fromLength; - $j = $toLength; - - while ($i > 0 && $j > 0) { - if ($from[$i - 1] === $to[$j - 1]) { - $common[] = $from[$i - 1]; - --$i; - --$j; - } else { - $o = ($j * $width) + $i; - - if ($matrix[$o - $width] > $matrix[$o - 1]) { - --$j; - } else { - --$i; - } - } - } - - return \array_reverse($common); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -final class Line -{ - public const ADDED = 1; - public const REMOVED = 2; - public const UNCHANGED = 3; - - /** - * @var int - */ - private $type; - - /** - * @var string - */ - private $content; - - public function __construct(int $type = self::UNCHANGED, string $content = '') - { - $this->type = $type; - $this->content = $content; - } - - public function getContent(): string - { - return $this->content; - } - - public function getType(): int - { - return $this->type; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; -use SebastianBergmann\Diff\Output\UnifiedDiffOutputBuilder; - -/** - * Diff implementation. - */ -final class Differ -{ - public const OLD = 0; - public const ADDED = 1; - public const REMOVED = 2; - public const DIFF_LINE_END_WARNING = 3; - public const NO_LINE_END_EOF_WARNING = 4; - - /** - * @var DiffOutputBuilderInterface - */ - private $outputBuilder; - - /** - * @param DiffOutputBuilderInterface $outputBuilder - * - * @throws InvalidArgumentException - */ - public function __construct($outputBuilder = null) - { - if ($outputBuilder instanceof DiffOutputBuilderInterface) { - $this->outputBuilder = $outputBuilder; - } elseif (null === $outputBuilder) { - $this->outputBuilder = new UnifiedDiffOutputBuilder; - } elseif (\is_string($outputBuilder)) { - // PHPUnit 6.1.4, 6.2.0, 6.2.1, 6.2.2, and 6.2.3 support - // @see https://github.com/sebastianbergmann/phpunit/issues/2734#issuecomment-314514056 - // @deprecated - $this->outputBuilder = new UnifiedDiffOutputBuilder($outputBuilder); - } else { - throw new InvalidArgumentException( - \sprintf( - 'Expected builder to be an instance of DiffOutputBuilderInterface, or a string, got %s.', - \is_object($outputBuilder) ? 'instance of "' . \get_class($outputBuilder) . '"' : \gettype($outputBuilder) . ' "' . $outputBuilder . '"' - ) - ); - } - } - - /** - * Returns the diff between two arrays or strings as string. - * - * @param array|string $from - * @param array|string $to - * @param null|LongestCommonSubsequenceCalculator $lcs - * - * @return string - */ - public function diff($from, $to, LongestCommonSubsequenceCalculator $lcs = null): string - { - $diff = $this->diffToArray( - $this->normalizeDiffInput($from), - $this->normalizeDiffInput($to), - $lcs - ); - - return $this->outputBuilder->getDiff($diff); - } - - /** - * Returns the diff between two arrays or strings as array. - * - * Each array element contains two elements: - * - [0] => mixed $token - * - [1] => 2|1|0 - * - * - 2: REMOVED: $token was removed from $from - * - 1: ADDED: $token was added to $from - * - 0: OLD: $token is not changed in $to - * - * @param array|string $from - * @param array|string $to - * @param LongestCommonSubsequenceCalculator $lcs - * - * @return array - */ - public function diffToArray($from, $to, LongestCommonSubsequenceCalculator $lcs = null): array - { - if (\is_string($from)) { - $from = $this->splitStringByLines($from); - } elseif (!\is_array($from)) { - throw new InvalidArgumentException('"from" must be an array or string.'); - } - - if (\is_string($to)) { - $to = $this->splitStringByLines($to); - } elseif (!\is_array($to)) { - throw new InvalidArgumentException('"to" must be an array or string.'); - } - - [$from, $to, $start, $end] = self::getArrayDiffParted($from, $to); - - if ($lcs === null) { - $lcs = $this->selectLcsImplementation($from, $to); - } - - $common = $lcs->calculate(\array_values($from), \array_values($to)); - $diff = []; - - foreach ($start as $token) { - $diff[] = [$token, self::OLD]; - } - - \reset($from); - \reset($to); - - foreach ($common as $token) { - while (($fromToken = \reset($from)) !== $token) { - $diff[] = [\array_shift($from), self::REMOVED]; - } - - while (($toToken = \reset($to)) !== $token) { - $diff[] = [\array_shift($to), self::ADDED]; - } - - $diff[] = [$token, self::OLD]; - - \array_shift($from); - \array_shift($to); - } - - while (($token = \array_shift($from)) !== null) { - $diff[] = [$token, self::REMOVED]; - } - - while (($token = \array_shift($to)) !== null) { - $diff[] = [$token, self::ADDED]; - } - - foreach ($end as $token) { - $diff[] = [$token, self::OLD]; - } - - if ($this->detectUnmatchedLineEndings($diff)) { - \array_unshift($diff, ["#Warning: Strings contain different line endings!\n", self::DIFF_LINE_END_WARNING]); - } - - return $diff; - } - - /** - * Casts variable to string if it is not a string or array. - * - * @param mixed $input - * - * @return array|string - */ - private function normalizeDiffInput($input) - { - if (!\is_array($input) && !\is_string($input)) { - return (string) $input; - } - - return $input; - } - - /** - * Checks if input is string, if so it will split it line-by-line. - * - * @param string $input - * - * @return array - */ - private function splitStringByLines(string $input): array - { - return \preg_split('/(.*\R)/', $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); - } - - /** - * @param array $from - * @param array $to - * - * @return LongestCommonSubsequenceCalculator - */ - private function selectLcsImplementation(array $from, array $to): LongestCommonSubsequenceCalculator - { - // We do not want to use the time-efficient implementation if its memory - // footprint will probably exceed this value. Note that the footprint - // calculation is only an estimation for the matrix and the LCS method - // will typically allocate a bit more memory than this. - $memoryLimit = 100 * 1024 * 1024; - - if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { - return new MemoryEfficientLongestCommonSubsequenceCalculator; - } - - return new TimeEfficientLongestCommonSubsequenceCalculator; - } - - /** - * Calculates the estimated memory footprint for the DP-based method. - * - * @param array $from - * @param array $to - * - * @return float|int - */ - private function calculateEstimatedFootprint(array $from, array $to) - { - $itemSize = PHP_INT_SIZE === 4 ? 76 : 144; - - return $itemSize * \min(\count($from), \count($to)) ** 2; - } - - /** - * Returns true if line ends don't match in a diff. - * - * @param array $diff - * - * @return bool - */ - private function detectUnmatchedLineEndings(array $diff): bool - { - $newLineBreaks = ['' => true]; - $oldLineBreaks = ['' => true]; - - foreach ($diff as $entry) { - if (self::OLD === $entry[1]) { - $ln = $this->getLinebreak($entry[0]); - $oldLineBreaks[$ln] = true; - $newLineBreaks[$ln] = true; - } elseif (self::ADDED === $entry[1]) { - $newLineBreaks[$this->getLinebreak($entry[0])] = true; - } elseif (self::REMOVED === $entry[1]) { - $oldLineBreaks[$this->getLinebreak($entry[0])] = true; - } - } - - // if either input or output is a single line without breaks than no warning should be raised - if (['' => true] === $newLineBreaks || ['' => true] === $oldLineBreaks) { - return false; - } - - // two way compare - foreach ($newLineBreaks as $break => $set) { - if (!isset($oldLineBreaks[$break])) { - return true; - } - } - - foreach ($oldLineBreaks as $break => $set) { - if (!isset($newLineBreaks[$break])) { - return true; - } - } - - return false; - } - - private function getLinebreak($line): string - { - if (!\is_string($line)) { - return ''; - } - - $lc = \substr($line, -1); - - if ("\r" === $lc) { - return "\r"; - } - - if ("\n" !== $lc) { - return ''; - } - - if ("\r\n" === \substr($line, -2)) { - return "\r\n"; - } - - return "\n"; - } - - private static function getArrayDiffParted(array &$from, array &$to): array - { - $start = []; - $end = []; - - \reset($to); - - foreach ($from as $k => $v) { - $toK = \key($to); - - if ($toK === $k && $v === $to[$k]) { - $start[$k] = $v; - - unset($from[$k], $to[$k]); - } else { - break; - } - } - - \end($from); - \end($to); - - do { - $fromK = \key($from); - $toK = \key($to); - - if (null === $fromK || null === $toK || \current($from) !== \current($to)) { - break; - } - - \prev($from); - \prev($to); - - $end = [$fromK => $from[$fromK]] + $end; - unset($from[$fromK], $to[$toK]); - } while (true); - - return [$from, $to, $start, $end]; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff\Output; - -/** - * Defines how an output builder should take a generated - * diff array and return a string representation of that diff. - */ -interface DiffOutputBuilderInterface -{ - public function getDiff(array $diff): string; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff\Output; - -use SebastianBergmann\Diff\ConfigurationException; -use SebastianBergmann\Diff\Differ; - -/** - * Strict Unified diff output builder. - * - * Generates (strict) Unified diff's (unidiffs) with hunks. - */ -final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface -{ - private static $default = [ - 'collapseRanges' => true, // ranges of length one are rendered with the trailing `,1` - 'commonLineThreshold' => 6, // number of same lines before ending a new hunk and creating a new one (if needed) - 'contextLines' => 3, // like `diff: -u, -U NUM, --unified[=NUM]`, for patch/git apply compatibility best to keep at least @ 3 - 'fromFile' => null, - 'fromFileDate' => null, - 'toFile' => null, - 'toFileDate' => null, - ]; - /** - * @var bool - */ - private $changed; - - /** - * @var bool - */ - private $collapseRanges; - - /** - * @var int >= 0 - */ - private $commonLineThreshold; - - /** - * @var string - */ - private $header; - - /** - * @var int >= 0 - */ - private $contextLines; - - public function __construct(array $options = []) - { - $options = \array_merge(self::$default, $options); - - if (!\is_bool($options['collapseRanges'])) { - throw new ConfigurationException('collapseRanges', 'a bool', $options['collapseRanges']); - } - - if (!\is_int($options['contextLines']) || $options['contextLines'] < 0) { - throw new ConfigurationException('contextLines', 'an int >= 0', $options['contextLines']); - } - - if (!\is_int($options['commonLineThreshold']) || $options['commonLineThreshold'] <= 0) { - throw new ConfigurationException('commonLineThreshold', 'an int > 0', $options['commonLineThreshold']); - } - - foreach (['fromFile', 'toFile'] as $option) { - if (!\is_string($options[$option])) { - throw new ConfigurationException($option, 'a string', $options[$option]); - } - } - - foreach (['fromFileDate', 'toFileDate'] as $option) { - if (null !== $options[$option] && !\is_string($options[$option])) { - throw new ConfigurationException($option, 'a string or ', $options[$option]); - } - } - - $this->header = \sprintf( - "--- %s%s\n+++ %s%s\n", - $options['fromFile'], - null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'], - $options['toFile'], - null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'] - ); - - $this->collapseRanges = $options['collapseRanges']; - $this->commonLineThreshold = $options['commonLineThreshold']; - $this->contextLines = $options['contextLines']; - } - - public function getDiff(array $diff): string - { - if (0 === \count($diff)) { - return ''; - } - - $this->changed = false; - - $buffer = \fopen('php://memory', 'r+b'); - \fwrite($buffer, $this->header); - - $this->writeDiffHunks($buffer, $diff); - - if (!$this->changed) { - \fclose($buffer); - - return ''; - } - - $diff = \stream_get_contents($buffer, -1, 0); - - \fclose($buffer); - - // If the last char is not a linebreak: add it. - // This might happen when both the `from` and `to` do not have a trailing linebreak - $last = \substr($diff, -1); - - return "\n" !== $last && "\r" !== $last - ? $diff . "\n" - : $diff - ; - } - - private function writeDiffHunks($output, array $diff): void - { - // detect "No newline at end of file" and insert into `$diff` if needed - - $upperLimit = \count($diff); - - if (0 === $diff[$upperLimit - 1][1]) { - $lc = \substr($diff[$upperLimit - 1][0], -1); - - if ("\n" !== $lc) { - \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => true, 2 => true]; - - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = \substr($diff[$i][0], -1); - - if ("\n" !== $lc) { - \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - - if (!\count($toFind)) { - break; - } - } - } - } - - // write hunks to output buffer - - $cutOff = \max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { // same - if (false === $hunkCapture) { - ++$fromStart; - ++$toStart; - - continue; - } - - ++$sameCount; - ++$toRange; - ++$fromRange; - - if ($sameCount === $cutOff) { - $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 - ? $hunkCapture - : $this->contextLines - ; - - // note: $contextEndOffset = $this->contextLines; - // - // because we never go beyond the end of the diff. - // with the cutoff/contextlines here the follow is never true; - // - // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { - // $contextEndOffset = count($diff) - 1; - // } - // - // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop - - $this->writeHunk( - $diff, - $hunkCapture - $contextStartOffset, - $i - $cutOff + $this->contextLines + 1, - $fromStart - $contextStartOffset, - $fromRange - $cutOff + $contextStartOffset + $this->contextLines, - $toStart - $contextStartOffset, - $toRange - $cutOff + $contextStartOffset + $this->contextLines, - $output - ); - - $fromStart += $fromRange; - $toStart += $toRange; - - $hunkCapture = false; - $sameCount = $toRange = $fromRange = 0; - } - - continue; - } - - $sameCount = 0; - - if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { - continue; - } - - $this->changed = true; - - if (false === $hunkCapture) { - $hunkCapture = $i; - } - - if (Differ::ADDED === $entry[1]) { // added - ++$toRange; - } - - if (Differ::REMOVED === $entry[1]) { // removed - ++$fromRange; - } - } - - if (false === $hunkCapture) { - return; - } - - // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, - // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold - - $contextStartOffset = $hunkCapture - $this->contextLines < 0 - ? $hunkCapture - : $this->contextLines - ; - - // prevent trying to write out more common lines than there are in the diff _and_ - // do not write more than configured through the context lines - $contextEndOffset = \min($sameCount, $this->contextLines); - - $fromRange -= $sameCount; - $toRange -= $sameCount; - - $this->writeHunk( - $diff, - $hunkCapture - $contextStartOffset, - $i - $sameCount + $contextEndOffset + 1, - $fromStart - $contextStartOffset, - $fromRange + $contextStartOffset + $contextEndOffset, - $toStart - $contextStartOffset, - $toRange + $contextStartOffset + $contextEndOffset, - $output - ); - } - - private function writeHunk( - array $diff, - int $diffStartIndex, - int $diffEndIndex, - int $fromStart, - int $fromRange, - int $toStart, - int $toRange, - $output - ): void { - \fwrite($output, '@@ -' . $fromStart); - - if (!$this->collapseRanges || 1 !== $fromRange) { - \fwrite($output, ',' . $fromRange); - } - - \fwrite($output, ' +' . $toStart); - - if (!$this->collapseRanges || 1 !== $toRange) { - \fwrite($output, ',' . $toRange); - } - - \fwrite($output, " @@\n"); - - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === Differ::ADDED) { - $this->changed = true; - \fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::REMOVED) { - $this->changed = true; - \fwrite($output, '-' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::OLD) { - \fwrite($output, ' ' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { - $this->changed = true; - \fwrite($output, $diff[$i][0]); - } - //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package - // skip - //} else { - // unknown/invalid - //} - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff\Output; - -abstract class AbstractChunkOutputBuilder implements DiffOutputBuilderInterface -{ - /** - * Takes input of the diff array and returns the common parts. - * Iterates through diff line by line. - * - * @param array $diff - * @param int $lineThreshold - * - * @return array - */ - protected function getCommonChunks(array $diff, int $lineThreshold = 5): array - { - $diffSize = \count($diff); - $capturing = false; - $chunkStart = 0; - $chunkSize = 0; - $commonChunks = []; - - for ($i = 0; $i < $diffSize; ++$i) { - if ($diff[$i][1] === 0 /* OLD */) { - if ($capturing === false) { - $capturing = true; - $chunkStart = $i; - $chunkSize = 0; - } else { - ++$chunkSize; - } - } elseif ($capturing !== false) { - if ($chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; - } - - $capturing = false; - } - } - - if ($capturing !== false && $chunkSize >= $lineThreshold) { - $commonChunks[$chunkStart] = $chunkStart + $chunkSize; - } - - return $commonChunks; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff\Output; - -use SebastianBergmann\Diff\Differ; - -/** - * Builds a diff string representation in unified diff format in chunks. - */ -final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder -{ - /** - * @var bool - */ - private $collapseRanges = true; - - /** - * @var int >= 0 - */ - private $commonLineThreshold = 6; - - /** - * @var int >= 0 - */ - private $contextLines = 3; - - /** - * @var string - */ - private $header; - - /** - * @var bool - */ - private $addLineNumbers; - - public function __construct(string $header = "--- Original\n+++ New\n", bool $addLineNumbers = false) - { - $this->header = $header; - $this->addLineNumbers = $addLineNumbers; - } - - public function getDiff(array $diff): string - { - $buffer = \fopen('php://memory', 'r+b'); - - if ('' !== $this->header) { - \fwrite($buffer, $this->header); - - if ("\n" !== \substr($this->header, -1, 1)) { - \fwrite($buffer, "\n"); - } - } - - if (0 !== \count($diff)) { - $this->writeDiffHunks($buffer, $diff); - } - - $diff = \stream_get_contents($buffer, -1, 0); - - \fclose($buffer); - - // If the last char is not a linebreak: add it. - // This might happen when both the `from` and `to` do not have a trailing linebreak - $last = \substr($diff, -1); - - return "\n" !== $last && "\r" !== $last - ? $diff . "\n" - : $diff - ; - } - - private function writeDiffHunks($output, array $diff): void - { - // detect "No newline at end of file" and insert into `$diff` if needed - - $upperLimit = \count($diff); - - if (0 === $diff[$upperLimit - 1][1]) { - $lc = \substr($diff[$upperLimit - 1][0], -1); - - if ("\n" !== $lc) { - \array_splice($diff, $upperLimit, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - } else { - // search back for the last `+` and `-` line, - // check if has trailing linebreak, else add under it warning under it - $toFind = [1 => true, 2 => true]; - - for ($i = $upperLimit - 1; $i >= 0; --$i) { - if (isset($toFind[$diff[$i][1]])) { - unset($toFind[$diff[$i][1]]); - $lc = \substr($diff[$i][0], -1); - - if ("\n" !== $lc) { - \array_splice($diff, $i + 1, 0, [["\n\\ No newline at end of file\n", Differ::NO_LINE_END_EOF_WARNING]]); - } - - if (!\count($toFind)) { - break; - } - } - } - } - - // write hunks to output buffer - - $cutOff = \max($this->commonLineThreshold, $this->contextLines); - $hunkCapture = false; - $sameCount = $toRange = $fromRange = 0; - $toStart = $fromStart = 1; - - foreach ($diff as $i => $entry) { - if (0 === $entry[1]) { // same - if (false === $hunkCapture) { - ++$fromStart; - ++$toStart; - - continue; - } - - ++$sameCount; - ++$toRange; - ++$fromRange; - - if ($sameCount === $cutOff) { - $contextStartOffset = ($hunkCapture - $this->contextLines) < 0 - ? $hunkCapture - : $this->contextLines - ; - - // note: $contextEndOffset = $this->contextLines; - // - // because we never go beyond the end of the diff. - // with the cutoff/contextlines here the follow is never true; - // - // if ($i - $cutOff + $this->contextLines + 1 > \count($diff)) { - // $contextEndOffset = count($diff) - 1; - // } - // - // ; that would be true for a trailing incomplete hunk case which is dealt with after this loop - - $this->writeHunk( - $diff, - $hunkCapture - $contextStartOffset, - $i - $cutOff + $this->contextLines + 1, - $fromStart - $contextStartOffset, - $fromRange - $cutOff + $contextStartOffset + $this->contextLines, - $toStart - $contextStartOffset, - $toRange - $cutOff + $contextStartOffset + $this->contextLines, - $output - ); - - $fromStart += $fromRange; - $toStart += $toRange; - - $hunkCapture = false; - $sameCount = $toRange = $fromRange = 0; - } - - continue; - } - - $sameCount = 0; - - if ($entry[1] === Differ::NO_LINE_END_EOF_WARNING) { - continue; - } - - if (false === $hunkCapture) { - $hunkCapture = $i; - } - - if (Differ::ADDED === $entry[1]) { - ++$toRange; - } - - if (Differ::REMOVED === $entry[1]) { - ++$fromRange; - } - } - - if (false === $hunkCapture) { - return; - } - - // we end here when cutoff (commonLineThreshold) was not reached, but we where capturing a hunk, - // do not render hunk till end automatically because the number of context lines might be less than the commonLineThreshold - - $contextStartOffset = $hunkCapture - $this->contextLines < 0 - ? $hunkCapture - : $this->contextLines - ; - - // prevent trying to write out more common lines than there are in the diff _and_ - // do not write more than configured through the context lines - $contextEndOffset = \min($sameCount, $this->contextLines); - - $fromRange -= $sameCount; - $toRange -= $sameCount; - - $this->writeHunk( - $diff, - $hunkCapture - $contextStartOffset, - $i - $sameCount + $contextEndOffset + 1, - $fromStart - $contextStartOffset, - $fromRange + $contextStartOffset + $contextEndOffset, - $toStart - $contextStartOffset, - $toRange + $contextStartOffset + $contextEndOffset, - $output - ); - } - - private function writeHunk( - array $diff, - int $diffStartIndex, - int $diffEndIndex, - int $fromStart, - int $fromRange, - int $toStart, - int $toRange, - $output - ): void { - if ($this->addLineNumbers) { - \fwrite($output, '@@ -' . $fromStart); - - if (!$this->collapseRanges || 1 !== $fromRange) { - \fwrite($output, ',' . $fromRange); - } - - \fwrite($output, ' +' . $toStart); - - if (!$this->collapseRanges || 1 !== $toRange) { - \fwrite($output, ',' . $toRange); - } - - \fwrite($output, " @@\n"); - } else { - \fwrite($output, "@@ @@\n"); - } - - for ($i = $diffStartIndex; $i < $diffEndIndex; ++$i) { - if ($diff[$i][1] === Differ::ADDED) { - \fwrite($output, '+' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::REMOVED) { - \fwrite($output, '-' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::OLD) { - \fwrite($output, ' ' . $diff[$i][0]); - } elseif ($diff[$i][1] === Differ::NO_LINE_END_EOF_WARNING) { - \fwrite($output, "\n"); // $diff[$i][0] - } else { /* Not changed (old) Differ::OLD or Warning Differ::DIFF_LINE_END_WARNING */ - \fwrite($output, ' ' . $diff[$i][0]); - } - } - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff\Output; - -use SebastianBergmann\Diff\Differ; - -/** - * Builds a diff string representation in a loose unified diff format - * listing only changes lines. Does not include line numbers. - */ -final class DiffOnlyOutputBuilder implements DiffOutputBuilderInterface -{ - /** - * @var string - */ - private $header; - - public function __construct(string $header = "--- Original\n+++ New\n") - { - $this->header = $header; - } - - public function getDiff(array $diff): string - { - $buffer = \fopen('php://memory', 'r+b'); - - if ('' !== $this->header) { - \fwrite($buffer, $this->header); - - if ("\n" !== \substr($this->header, -1, 1)) { - \fwrite($buffer, "\n"); - } - } - - foreach ($diff as $diffEntry) { - if ($diffEntry[1] === Differ::ADDED) { - \fwrite($buffer, '+' . $diffEntry[0]); - } elseif ($diffEntry[1] === Differ::REMOVED) { - \fwrite($buffer, '-' . $diffEntry[0]); - } elseif ($diffEntry[1] === Differ::DIFF_LINE_END_WARNING) { - \fwrite($buffer, ' ' . $diffEntry[0]); - - continue; // Warnings should not be tested for line break, it will always be there - } else { /* Not changed (old) 0 */ - continue; // we didn't write the non changs line, so do not add a line break either - } - - $lc = \substr($diffEntry[0], -1); - - if ($lc !== "\n" && $lc !== "\r") { - \fwrite($buffer, "\n"); // \No newline at end of file - } - } - - $diff = \stream_get_contents($buffer, -1, 0); - \fclose($buffer); - - return $diff; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -final class Diff -{ - /** - * @var string - */ - private $from; - - /** - * @var string - */ - private $to; - - /** - * @var Chunk[] - */ - private $chunks; - - /** - * @param string $from - * @param string $to - * @param Chunk[] $chunks - */ - public function __construct(string $from, string $to, array $chunks = []) - { - $this->from = $from; - $this->to = $to; - $this->chunks = $chunks; - } - - public function getFrom(): string - { - return $this->from; - } - - public function getTo(): string - { - return $this->to; - } - - /** - * @return Chunk[] - */ - public function getChunks(): array - { - return $this->chunks; - } - - /** - * @param Chunk[] $chunks - */ - public function setChunks(array $chunks): void - { - $this->chunks = $chunks; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -/** - * Unified diff parser. - */ -final class Parser -{ - /** - * @param string $string - * - * @return Diff[] - */ - public function parse(string $string): array - { - $lines = \preg_split('(\r\n|\r|\n)', $string); - - if (!empty($lines) && $lines[\count($lines) - 1] === '') { - \array_pop($lines); - } - - $lineCount = \count($lines); - $diffs = []; - $diff = null; - $collected = []; - - for ($i = 0; $i < $lineCount; ++$i) { - if (\preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && - \preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { - if ($diff !== null) { - $this->parseFileDiff($diff, $collected); - - $diffs[] = $diff; - $collected = []; - } - - $diff = new Diff($fromMatch['file'], $toMatch['file']); - - ++$i; - } else { - if (\preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { - continue; - } - - $collected[] = $lines[$i]; - } - } - - if ($diff !== null && \count($collected)) { - $this->parseFileDiff($diff, $collected); - - $diffs[] = $diff; - } - - return $diffs; - } - - private function parseFileDiff(Diff $diff, array $lines): void - { - $chunks = []; - $chunk = null; - - foreach ($lines as $line) { - if (\preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { - $chunk = new Chunk( - (int) $match['start'], - isset($match['startrange']) ? \max(1, (int) $match['startrange']) : 1, - (int) $match['end'], - isset($match['endrange']) ? \max(1, (int) $match['endrange']) : 1 - ); - - $chunks[] = $chunk; - $diffLines = []; - - continue; - } - - if (\preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { - $type = Line::UNCHANGED; - - if ($match['type'] === '+') { - $type = Line::ADDED; - } elseif ($match['type'] === '-') { - $type = Line::REMOVED; - } - - $diffLines[] = new Line($type, $match['line']); - - if (null !== $chunk) { - $chunk->setLines($diffLines); - } - } - } - - $diff->setChunks($chunks); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -final class Chunk -{ - /** - * @var int - */ - private $start; - - /** - * @var int - */ - private $startRange; - - /** - * @var int - */ - private $end; - - /** - * @var int - */ - private $endRange; - - /** - * @var Line[] - */ - private $lines; - - public function __construct(int $start = 0, int $startRange = 1, int $end = 0, int $endRange = 1, array $lines = []) - { - $this->start = $start; - $this->startRange = $startRange; - $this->end = $end; - $this->endRange = $endRange; - $this->lines = $lines; - } - - public function getStart(): int - { - return $this->start; - } - - public function getStartRange(): int - { - return $this->startRange; - } - - public function getEnd(): int - { - return $this->end; - } - - public function getEndRange(): int - { - return $this->endRange; - } - - /** - * @return Line[] - */ - public function getLines(): array - { - return $this->lines; - } - - /** - * @param Line[] $lines - */ - public function setLines(array $lines): void - { - foreach ($lines as $line) { - if (!$line instanceof Line) { - throw new InvalidArgumentException; - } - } - - $this->lines = $lines; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Diff; - -interface LongestCommonSubsequenceCalculator -{ - /** - * Calculates the longest common subsequence of two arrays. - * - * @param array $from - * @param array $to - * - * @return array - */ - public function calculate(array $from, array $to): array; -} -sebastian/diff - -Copyright (c) 2002-2019, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Exception that is raised when @covers must be used but is not. - */ -final class MissingCoversAnnotationException extends RuntimeException -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -class RuntimeException extends \RuntimeException implements Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Exception interface for php-code-coverage component. - */ -interface Exception -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Exception that is raised when covered code is not executed. - */ -final class CoveredCodeNotExecutedException extends RuntimeException -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -final class InvalidArgumentException extends \InvalidArgumentException implements Exception -{ - /** - * @param int $argument - * @param string $type - * @param null|mixed $value - * - * @return InvalidArgumentException - */ - public static function create($argument, $type, $value = null): self - { - $stack = \debug_backtrace(0); - - return new self( - \sprintf( - 'Argument #%d%sof %s::%s() must be a %s', - $argument, - $value !== null ? ' (' . \gettype($value) . '#' . $value . ')' : ' (No Value) ', - $stack[1]['class'], - $stack[1]['function'], - $type - ) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Exception that is raised when code is unintentionally covered. - */ -final class UnintentionallyCoveredCodeException extends RuntimeException -{ - /** - * @var array - */ - private $unintentionallyCoveredUnits = []; - - public function __construct(array $unintentionallyCoveredUnits) - { - $this->unintentionallyCoveredUnits = $unintentionallyCoveredUnits; - - parent::__construct($this->toString()); - } - - public function getUnintentionallyCoveredUnits(): array - { - return $this->unintentionallyCoveredUnits; - } - - private function toString(): string - { - $message = ''; - - foreach ($this->unintentionallyCoveredUnits as $unit) { - $message .= '- ' . $unit . "\n"; - } - - return $message; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Node\File; -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Generates a Clover XML logfile from a code coverage object. - */ -final class Clover -{ - /** - * @throws \RuntimeException - */ - public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string - { - $xmlDocument = new \DOMDocument('1.0', 'UTF-8'); - $xmlDocument->formatOutput = true; - - $xmlCoverage = $xmlDocument->createElement('coverage'); - $xmlCoverage->setAttribute('generated', (int) $_SERVER['REQUEST_TIME']); - $xmlDocument->appendChild($xmlCoverage); - - $xmlProject = $xmlDocument->createElement('project'); - $xmlProject->setAttribute('timestamp', (int) $_SERVER['REQUEST_TIME']); - - if (\is_string($name)) { - $xmlProject->setAttribute('name', $name); - } - - $xmlCoverage->appendChild($xmlProject); - - $packages = []; - $report = $coverage->getReport(); - - foreach ($report as $item) { - if (!$item instanceof File) { - continue; - } - - /* @var File $item */ - - $xmlFile = $xmlDocument->createElement('file'); - $xmlFile->setAttribute('name', $item->getPath()); - - $classes = $item->getClassesAndTraits(); - $coverageData = $item->getCoverageData(); - $lines = []; - $namespace = 'global'; - - foreach ($classes as $className => $class) { - $classStatements = 0; - $coveredClassStatements = 0; - $coveredMethods = 0; - $classMethods = 0; - - foreach ($class['methods'] as $methodName => $method) { - if ($method['executableLines'] == 0) { - continue; - } - - $classMethods++; - $classStatements += $method['executableLines']; - $coveredClassStatements += $method['executedLines']; - - if ($method['coverage'] == 100) { - $coveredMethods++; - } - - $methodCount = 0; - - foreach (\range($method['startLine'], $method['endLine']) as $line) { - if (isset($coverageData[$line]) && ($coverageData[$line] !== null)) { - $methodCount = \max($methodCount, \count($coverageData[$line])); - } - } - - $lines[$method['startLine']] = [ - 'ccn' => $method['ccn'], - 'count' => $methodCount, - 'crap' => $method['crap'], - 'type' => 'method', - 'visibility' => $method['visibility'], - 'name' => $methodName, - ]; - } - - if (!empty($class['package']['namespace'])) { - $namespace = $class['package']['namespace']; - } - - $xmlClass = $xmlDocument->createElement('class'); - $xmlClass->setAttribute('name', $className); - $xmlClass->setAttribute('namespace', $namespace); - - if (!empty($class['package']['fullPackage'])) { - $xmlClass->setAttribute( - 'fullPackage', - $class['package']['fullPackage'] - ); - } - - if (!empty($class['package']['category'])) { - $xmlClass->setAttribute( - 'category', - $class['package']['category'] - ); - } - - if (!empty($class['package']['package'])) { - $xmlClass->setAttribute( - 'package', - $class['package']['package'] - ); - } - - if (!empty($class['package']['subpackage'])) { - $xmlClass->setAttribute( - 'subpackage', - $class['package']['subpackage'] - ); - } - - $xmlFile->appendChild($xmlClass); - - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('complexity', $class['ccn']); - $xmlMetrics->setAttribute('methods', $classMethods); - $xmlMetrics->setAttribute('coveredmethods', $coveredMethods); - $xmlMetrics->setAttribute('conditionals', 0); - $xmlMetrics->setAttribute('coveredconditionals', 0); - $xmlMetrics->setAttribute('statements', $classStatements); - $xmlMetrics->setAttribute('coveredstatements', $coveredClassStatements); - $xmlMetrics->setAttribute('elements', $classMethods + $classStatements /* + conditionals */); - $xmlMetrics->setAttribute('coveredelements', $coveredMethods + $coveredClassStatements /* + coveredconditionals */); - $xmlClass->appendChild($xmlMetrics); - } - - foreach ($coverageData as $line => $data) { - if ($data === null || isset($lines[$line])) { - continue; - } - - $lines[$line] = [ - 'count' => \count($data), 'type' => 'stmt', - ]; - } - - \ksort($lines); - - foreach ($lines as $line => $data) { - $xmlLine = $xmlDocument->createElement('line'); - $xmlLine->setAttribute('num', $line); - $xmlLine->setAttribute('type', $data['type']); - - if (isset($data['name'])) { - $xmlLine->setAttribute('name', $data['name']); - } - - if (isset($data['visibility'])) { - $xmlLine->setAttribute('visibility', $data['visibility']); - } - - if (isset($data['ccn'])) { - $xmlLine->setAttribute('complexity', $data['ccn']); - } - - if (isset($data['crap'])) { - $xmlLine->setAttribute('crap', $data['crap']); - } - - $xmlLine->setAttribute('count', $data['count']); - $xmlFile->appendChild($xmlLine); - } - - $linesOfCode = $item->getLinesOfCode(); - - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); - $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); - $xmlMetrics->setAttribute('classes', $item->getNumClassesAndTraits()); - $xmlMetrics->setAttribute('methods', $item->getNumMethods()); - $xmlMetrics->setAttribute('coveredmethods', $item->getNumTestedMethods()); - $xmlMetrics->setAttribute('conditionals', 0); - $xmlMetrics->setAttribute('coveredconditionals', 0); - $xmlMetrics->setAttribute('statements', $item->getNumExecutableLines()); - $xmlMetrics->setAttribute('coveredstatements', $item->getNumExecutedLines()); - $xmlMetrics->setAttribute('elements', $item->getNumMethods() + $item->getNumExecutableLines() /* + conditionals */); - $xmlMetrics->setAttribute('coveredelements', $item->getNumTestedMethods() + $item->getNumExecutedLines() /* + coveredconditionals */); - $xmlFile->appendChild($xmlMetrics); - - if ($namespace === 'global') { - $xmlProject->appendChild($xmlFile); - } else { - if (!isset($packages[$namespace])) { - $packages[$namespace] = $xmlDocument->createElement( - 'package' - ); - - $packages[$namespace]->setAttribute('name', $namespace); - $xmlProject->appendChild($packages[$namespace]); - } - - $packages[$namespace]->appendChild($xmlFile); - } - } - - $linesOfCode = $report->getLinesOfCode(); - - $xmlMetrics = $xmlDocument->createElement('metrics'); - $xmlMetrics->setAttribute('files', \count($report)); - $xmlMetrics->setAttribute('loc', $linesOfCode['loc']); - $xmlMetrics->setAttribute('ncloc', $linesOfCode['ncloc']); - $xmlMetrics->setAttribute('classes', $report->getNumClassesAndTraits()); - $xmlMetrics->setAttribute('methods', $report->getNumMethods()); - $xmlMetrics->setAttribute('coveredmethods', $report->getNumTestedMethods()); - $xmlMetrics->setAttribute('conditionals', 0); - $xmlMetrics->setAttribute('coveredconditionals', 0); - $xmlMetrics->setAttribute('statements', $report->getNumExecutableLines()); - $xmlMetrics->setAttribute('coveredstatements', $report->getNumExecutedLines()); - $xmlMetrics->setAttribute('elements', $report->getNumMethods() + $report->getNumExecutableLines() /* + conditionals */); - $xmlMetrics->setAttribute('coveredelements', $report->getNumTestedMethods() + $report->getNumExecutedLines() /* + coveredconditionals */); - $xmlProject->appendChild($xmlMetrics); - - $buffer = $xmlDocument->saveXML(); - - if ($target !== null) { - if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); - } - - if (@\file_put_contents($target, $buffer) === false) { - throw new RuntimeException( - \sprintf( - 'Could not write to "%s', - $target - ) - ); - } - } - - return $buffer; - } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Uses var_export() to write a SebastianBergmann\CodeCoverage\CodeCoverage object to a file. - */ -final class PHP -{ - /** - * @throws \SebastianBergmann\CodeCoverage\RuntimeException - */ - public function process(CodeCoverage $coverage, ?string $target = null): string - { - $filter = $coverage->filter(); - - $buffer = \sprintf( - 'setData(%s); -$coverage->setTests(%s); - -$filter = $coverage->filter(); -$filter->setWhitelistedFiles(%s); - -return $coverage;', - \var_export($coverage->getData(true), 1), - \var_export($coverage->getTests(), 1), - \var_export($filter->getWhitelistedFiles(), 1) - ); - - if ($target !== null) { - if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); - } - - if (@\file_put_contents($target, $buffer) === false) { - throw new RuntimeException( - \sprintf( - 'Could not write to "%s', - $target - ) - ); - } - } - - return $buffer; - } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Tests -{ - private $contextNode; - - private $codeMap = [ - -1 => 'UNKNOWN', // PHPUnit_Runner_BaseTestRunner::STATUS_UNKNOWN - 0 => 'PASSED', // PHPUnit_Runner_BaseTestRunner::STATUS_PASSED - 1 => 'SKIPPED', // PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED - 2 => 'INCOMPLETE', // PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE - 3 => 'FAILURE', // PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE - 4 => 'ERROR', // PHPUnit_Runner_BaseTestRunner::STATUS_ERROR - 5 => 'RISKY', // PHPUnit_Runner_BaseTestRunner::STATUS_RISKY - 6 => 'WARNING', // PHPUnit_Runner_BaseTestRunner::STATUS_WARNING - ]; - - public function __construct(\DOMElement $context) - { - $this->contextNode = $context; - } - - public function addTest(string $test, array $result): void - { - $node = $this->contextNode->appendChild( - $this->contextNode->ownerDocument->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'test' - ) - ); - - $node->setAttribute('name', $test); - $node->setAttribute('size', $result['size']); - $node->setAttribute('result', (int) $result['status']); - $node->setAttribute('status', $this->codeMap[(int) $result['status']]); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Directory extends Node -{ -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use TheSeer\Tokenizer\NamespaceUri; -use TheSeer\Tokenizer\Tokenizer; -use TheSeer\Tokenizer\XMLSerializer; - -final class Source -{ - /** @var \DOMElement */ - private $context; - - public function __construct(\DOMElement $context) - { - $this->context = $context; - } - - public function setSourceCode(string $source): void - { - $context = $this->context; - - $tokens = (new Tokenizer())->parse($source); - $srcDom = (new XMLSerializer(new NamespaceUri($context->namespaceURI)))->toDom($tokens); - - $context->parentNode->replaceChild( - $context->ownerDocument->importNode($srcDom->documentElement, true), - $context - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\CodeCoverage\Util; - -final class Totals -{ - /** - * @var \DOMNode - */ - private $container; - - /** - * @var \DOMElement - */ - private $linesNode; - - /** - * @var \DOMElement - */ - private $methodsNode; - - /** - * @var \DOMElement - */ - private $functionsNode; - - /** - * @var \DOMElement - */ - private $classesNode; - - /** - * @var \DOMElement - */ - private $traitsNode; - - public function __construct(\DOMElement $container) - { - $this->container = $container; - $dom = $container->ownerDocument; - - $this->linesNode = $dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'lines' - ); - - $this->methodsNode = $dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'methods' - ); - - $this->functionsNode = $dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'functions' - ); - - $this->classesNode = $dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'classes' - ); - - $this->traitsNode = $dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'traits' - ); - - $container->appendChild($this->linesNode); - $container->appendChild($this->methodsNode); - $container->appendChild($this->functionsNode); - $container->appendChild($this->classesNode); - $container->appendChild($this->traitsNode); - } - - public function getContainer(): \DOMNode - { - return $this->container; - } - - public function setNumLines(int $loc, int $cloc, int $ncloc, int $executable, int $executed): void - { - $this->linesNode->setAttribute('total', $loc); - $this->linesNode->setAttribute('comments', $cloc); - $this->linesNode->setAttribute('code', $ncloc); - $this->linesNode->setAttribute('executable', $executable); - $this->linesNode->setAttribute('executed', $executed); - $this->linesNode->setAttribute( - 'percent', - $executable === 0 ? 0 : \sprintf('%01.2F', Util::percent($executed, $executable)) - ); - } - - public function setNumClasses(int $count, int $tested): void - { - $this->classesNode->setAttribute('count', $count); - $this->classesNode->setAttribute('tested', $tested); - $this->classesNode->setAttribute( - 'percent', - $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) - ); - } - - public function setNumTraits(int $count, int $tested): void - { - $this->traitsNode->setAttribute('count', $count); - $this->traitsNode->setAttribute('tested', $tested); - $this->traitsNode->setAttribute( - 'percent', - $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) - ); - } - - public function setNumMethods(int $count, int $tested): void - { - $this->methodsNode->setAttribute('count', $count); - $this->methodsNode->setAttribute('tested', $tested); - $this->methodsNode->setAttribute( - 'percent', - $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) - ); - } - - public function setNumFunctions(int $count, int $tested): void - { - $this->functionsNode->setAttribute('count', $count); - $this->functionsNode->setAttribute('tested', $tested); - $this->functionsNode->setAttribute( - 'percent', - $count === 0 ? 0 : \sprintf('%01.2F', Util::percent($tested, $count)) - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\CodeCoverage\RuntimeException; - -final class Coverage -{ - /** - * @var \XMLWriter - */ - private $writer; - - /** - * @var \DOMElement - */ - private $contextNode; - - /** - * @var bool - */ - private $finalized = false; - - public function __construct(\DOMElement $context, string $line) - { - $this->contextNode = $context; - - $this->writer = new \XMLWriter(); - $this->writer->openMemory(); - $this->writer->startElementNS(null, $context->nodeName, 'https://schema.phpunit.de/coverage/1.0'); - $this->writer->writeAttribute('nr', $line); - } - - /** - * @throws RuntimeException - */ - public function addTest(string $test): void - { - if ($this->finalized) { - throw new RuntimeException('Coverage Report already finalized'); - } - - $this->writer->startElement('covered'); - $this->writer->writeAttribute('by', $test); - $this->writer->endElement(); - } - - public function finalize(): void - { - $this->writer->endElement(); - - $fragment = $this->contextNode->ownerDocument->createDocumentFragment(); - $fragment->appendXML($this->writer->outputMemory()); - - $this->contextNode->parentNode->replaceChild( - $fragment, - $this->contextNode - ); - - $this->finalized = true; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Report extends File -{ - public function __construct(string $name) - { - $dom = new \DOMDocument(); - $dom->loadXML(''); - - $contextNode = $dom->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'file' - )->item(0); - - parent::__construct($contextNode); - - $this->setName($name); - } - - public function asDom(): \DOMDocument - { - return $this->getDomDocument(); - } - - public function getFunctionObject($name): Method - { - $node = $this->getContextNode()->appendChild( - $this->getDomDocument()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'function' - ) - ); - - return new Method($node, $name); - } - - public function getClassObject($name): Unit - { - return $this->getUnitObject('class', $name); - } - - public function getTraitObject($name): Unit - { - return $this->getUnitObject('trait', $name); - } - - public function getSource(): Source - { - $source = $this->getContextNode()->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'source' - )->item(0); - - if (!$source) { - $source = $this->getContextNode()->appendChild( - $this->getDomDocument()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'source' - ) - ); - } - - return new Source($source); - } - - private function setName($name): void - { - $this->getContextNode()->setAttribute('name', \basename($name)); - $this->getContextNode()->setAttribute('path', \dirname($name)); - } - - private function getUnitObject($tagName, $name): Unit - { - $node = $this->getContextNode()->appendChild( - $this->getDomDocument()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - $tagName - ) - ); - - return new Unit($node, $name); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Method -{ - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $context, string $name) - { - $this->contextNode = $context; - - $this->setName($name); - } - - public function setSignature(string $signature): void - { - $this->contextNode->setAttribute('signature', $signature); - } - - public function setLines(string $start, ?string $end = null): void - { - $this->contextNode->setAttribute('start', $start); - - if ($end !== null) { - $this->contextNode->setAttribute('end', $end); - } - } - - public function setTotals(string $executable, string $executed, string $coverage): void - { - $this->contextNode->setAttribute('executable', $executable); - $this->contextNode->setAttribute('executed', $executed); - $this->contextNode->setAttribute('coverage', $coverage); - } - - public function setCrap(string $crap): void - { - $this->contextNode->setAttribute('crap', $crap); - } - - private function setName(string $name): void - { - $this->contextNode->setAttribute('name', $name); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Node\AbstractNode; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use SebastianBergmann\CodeCoverage\Node\File as FileNode; -use SebastianBergmann\CodeCoverage\RuntimeException; -use SebastianBergmann\CodeCoverage\Version; -use SebastianBergmann\Environment\Runtime; - -final class Facade -{ - /** - * @var string - */ - private $target; - - /** - * @var Project - */ - private $project; - - /** - * @var string - */ - private $phpUnitVersion; - - public function __construct(string $version) - { - $this->phpUnitVersion = $version; - } - - /** - * @throws RuntimeException - */ - public function process(CodeCoverage $coverage, string $target): void - { - if (\substr($target, -1, 1) !== \DIRECTORY_SEPARATOR) { - $target .= \DIRECTORY_SEPARATOR; - } - - $this->target = $target; - $this->initTargetDirectory($target); - - $report = $coverage->getReport(); - - $this->project = new Project( - $coverage->getReport()->getName() - ); - - $this->setBuildInformation(); - $this->processTests($coverage->getTests()); - $this->processDirectory($report, $this->project); - - $this->saveDocument($this->project->asDom(), 'index'); - } - - private function setBuildInformation(): void - { - $buildNode = $this->project->getBuildInformation(); - $buildNode->setRuntimeInformation(new Runtime()); - $buildNode->setBuildTime(\DateTime::createFromFormat('U', $_SERVER['REQUEST_TIME'])); - $buildNode->setGeneratorVersions($this->phpUnitVersion, Version::id()); - } - - /** - * @throws RuntimeException - */ - private function initTargetDirectory(string $directory): void - { - if (\file_exists($directory)) { - if (!\is_dir($directory)) { - throw new RuntimeException( - "'$directory' exists but is not a directory." - ); - } - - if (!\is_writable($directory)) { - throw new RuntimeException( - "'$directory' exists but is not writable." - ); - } - } elseif (!$this->createDirectory($directory)) { - throw new RuntimeException( - "'$directory' could not be created." - ); - } - } - - private function processDirectory(DirectoryNode $directory, Node $context): void - { - $directoryName = $directory->getName(); - - if ($this->project->getProjectSourceDirectory() === $directoryName) { - $directoryName = '/'; - } - - $directoryObject = $context->addDirectory($directoryName); - - $this->setTotals($directory, $directoryObject->getTotals()); - - foreach ($directory->getDirectories() as $node) { - $this->processDirectory($node, $directoryObject); - } - - foreach ($directory->getFiles() as $node) { - $this->processFile($node, $directoryObject); - } - } - - /** - * @throws RuntimeException - */ - private function processFile(FileNode $file, Directory $context): void - { - $fileObject = $context->addFile( - $file->getName(), - $file->getId() . '.xml' - ); - - $this->setTotals($file, $fileObject->getTotals()); - - $path = \substr( - $file->getPath(), - \strlen($this->project->getProjectSourceDirectory()) - ); - - $fileReport = new Report($path); - - $this->setTotals($file, $fileReport->getTotals()); - - foreach ($file->getClassesAndTraits() as $unit) { - $this->processUnit($unit, $fileReport); - } - - foreach ($file->getFunctions() as $function) { - $this->processFunction($function, $fileReport); - } - - foreach ($file->getCoverageData() as $line => $tests) { - if (!\is_array($tests) || \count($tests) === 0) { - continue; - } - - $coverage = $fileReport->getLineCoverage($line); - - foreach ($tests as $test) { - $coverage->addTest($test); - } - - $coverage->finalize(); - } - - $fileReport->getSource()->setSourceCode( - \file_get_contents($file->getPath()) - ); - - $this->saveDocument($fileReport->asDom(), $file->getId()); - } - - private function processUnit(array $unit, Report $report): void - { - if (isset($unit['className'])) { - $unitObject = $report->getClassObject($unit['className']); - } else { - $unitObject = $report->getTraitObject($unit['traitName']); - } - - $unitObject->setLines( - $unit['startLine'], - $unit['executableLines'], - $unit['executedLines'] - ); - - $unitObject->setCrap($unit['crap']); - - $unitObject->setPackage( - $unit['package']['fullPackage'], - $unit['package']['package'], - $unit['package']['subpackage'], - $unit['package']['category'] - ); - - $unitObject->setNamespace($unit['package']['namespace']); - - foreach ($unit['methods'] as $method) { - $methodObject = $unitObject->addMethod($method['methodName']); - $methodObject->setSignature($method['signature']); - $methodObject->setLines($method['startLine'], $method['endLine']); - $methodObject->setCrap($method['crap']); - $methodObject->setTotals( - $method['executableLines'], - $method['executedLines'], - $method['coverage'] - ); - } - } - - private function processFunction(array $function, Report $report): void - { - $functionObject = $report->getFunctionObject($function['functionName']); - - $functionObject->setSignature($function['signature']); - $functionObject->setLines($function['startLine']); - $functionObject->setCrap($function['crap']); - $functionObject->setTotals($function['executableLines'], $function['executedLines'], $function['coverage']); - } - - private function processTests(array $tests): void - { - $testsObject = $this->project->getTests(); - - foreach ($tests as $test => $result) { - if ($test === 'UNCOVERED_FILES_FROM_WHITELIST') { - continue; - } - - $testsObject->addTest($test, $result); - } - } - - private function setTotals(AbstractNode $node, Totals $totals): void - { - $loc = $node->getLinesOfCode(); - - $totals->setNumLines( - $loc['loc'], - $loc['cloc'], - $loc['ncloc'], - $node->getNumExecutableLines(), - $node->getNumExecutedLines() - ); - - $totals->setNumClasses( - $node->getNumClasses(), - $node->getNumTestedClasses() - ); - - $totals->setNumTraits( - $node->getNumTraits(), - $node->getNumTestedTraits() - ); - - $totals->setNumMethods( - $node->getNumMethods(), - $node->getNumTestedMethods() - ); - - $totals->setNumFunctions( - $node->getNumFunctions(), - $node->getNumTestedFunctions() - ); - } - - private function getTargetDirectory(): string - { - return $this->target; - } - - /** - * @throws RuntimeException - */ - private function saveDocument(\DOMDocument $document, string $name): void - { - $filename = \sprintf('%s/%s.xml', $this->getTargetDirectory(), $name); - - $document->formatOutput = true; - $document->preserveWhiteSpace = false; - $this->initTargetDirectory(\dirname($filename)); - - $document->save($filename); - } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Unit -{ - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $context, string $name) - { - $this->contextNode = $context; - - $this->setName($name); - } - - public function setLines(int $start, int $executable, int $executed): void - { - $this->contextNode->setAttribute('start', $start); - $this->contextNode->setAttribute('executable', $executable); - $this->contextNode->setAttribute('executed', $executed); - } - - public function setCrap(float $crap): void - { - $this->contextNode->setAttribute('crap', $crap); - } - - public function setPackage(string $full, string $package, string $sub, string $category): void - { - $node = $this->contextNode->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'package' - )->item(0); - - if (!$node) { - $node = $this->contextNode->appendChild( - $this->contextNode->ownerDocument->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'package' - ) - ); - } - - $node->setAttribute('full', $full); - $node->setAttribute('name', $package); - $node->setAttribute('sub', $sub); - $node->setAttribute('category', $category); - } - - public function setNamespace(string $namespace): void - { - $node = $this->contextNode->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'namespace' - )->item(0); - - if (!$node) { - $node = $this->contextNode->appendChild( - $this->contextNode->ownerDocument->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'namespace' - ) - ); - } - - $node->setAttribute('name', $namespace); - } - - public function addMethod(string $name): Method - { - $node = $this->contextNode->appendChild( - $this->contextNode->ownerDocument->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'method' - ) - ); - - return new Method($node, $name); - } - - private function setName(string $name): void - { - $this->contextNode->setAttribute('name', $name); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -use SebastianBergmann\Environment\Runtime; - -final class BuildInformation -{ - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $contextNode) - { - $this->contextNode = $contextNode; - } - - public function setRuntimeInformation(Runtime $runtime): void - { - $runtimeNode = $this->getNodeByName('runtime'); - - $runtimeNode->setAttribute('name', $runtime->getName()); - $runtimeNode->setAttribute('version', $runtime->getVersion()); - $runtimeNode->setAttribute('url', $runtime->getVendorUrl()); - - $driverNode = $this->getNodeByName('driver'); - - if ($runtime->hasPHPDBGCodeCoverage()) { - $driverNode->setAttribute('name', 'phpdbg'); - $driverNode->setAttribute('version', \constant('PHPDBG_VERSION')); - } - - if ($runtime->hasXdebug()) { - $driverNode->setAttribute('name', 'xdebug'); - $driverNode->setAttribute('version', \phpversion('xdebug')); - } - } - - public function setBuildTime(\DateTime $date): void - { - $this->contextNode->setAttribute('time', $date->format('D M j G:i:s T Y')); - } - - public function setGeneratorVersions(string $phpUnitVersion, string $coverageVersion): void - { - $this->contextNode->setAttribute('phpunit', $phpUnitVersion); - $this->contextNode->setAttribute('coverage', $coverageVersion); - } - - private function getNodeByName(string $name): \DOMElement - { - $node = $this->contextNode->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - $name - )->item(0); - - if (!$node) { - $node = $this->contextNode->appendChild( - $this->contextNode->ownerDocument->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - $name - ) - ); - } - - return $node; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -class File -{ - /** - * @var \DOMDocument - */ - private $dom; - - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $context) - { - $this->dom = $context->ownerDocument; - $this->contextNode = $context; - } - - public function getTotals(): Totals - { - $totalsContainer = $this->contextNode->firstChild; - - if (!$totalsContainer) { - $totalsContainer = $this->contextNode->appendChild( - $this->dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'totals' - ) - ); - } - - return new Totals($totalsContainer); - } - - public function getLineCoverage(string $line): Coverage - { - $coverage = $this->contextNode->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'coverage' - )->item(0); - - if (!$coverage) { - $coverage = $this->contextNode->appendChild( - $this->dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'coverage' - ) - ); - } - - $lineNode = $coverage->appendChild( - $this->dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'line' - ) - ); - - return new Coverage($lineNode, $line); - } - - protected function getContextNode(): \DOMElement - { - return $this->contextNode; - } - - protected function getDomDocument(): \DOMDocument - { - return $this->dom; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -abstract class Node -{ - /** - * @var \DOMDocument - */ - private $dom; - - /** - * @var \DOMElement - */ - private $contextNode; - - public function __construct(\DOMElement $context) - { - $this->setContextNode($context); - } - - public function getDom(): \DOMDocument - { - return $this->dom; - } - - public function getTotals(): Totals - { - $totalsContainer = $this->getContextNode()->firstChild; - - if (!$totalsContainer) { - $totalsContainer = $this->getContextNode()->appendChild( - $this->dom->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'totals' - ) - ); - } - - return new Totals($totalsContainer); - } - - public function addDirectory(string $name): Directory - { - $dirNode = $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'directory' - ); - - $dirNode->setAttribute('name', $name); - $this->getContextNode()->appendChild($dirNode); - - return new Directory($dirNode); - } - - public function addFile(string $name, string $href): File - { - $fileNode = $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'file' - ); - - $fileNode->setAttribute('name', $name); - $fileNode->setAttribute('href', $href); - $this->getContextNode()->appendChild($fileNode); - - return new File($fileNode); - } - - protected function setContextNode(\DOMElement $context): void - { - $this->dom = $context->ownerDocument; - $this->contextNode = $context; - } - - protected function getContextNode(): \DOMElement - { - return $this->contextNode; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Xml; - -final class Project extends Node -{ - public function __construct(string $directory) - { - $this->init(); - $this->setProjectSourceDirectory($directory); - } - - public function getProjectSourceDirectory(): string - { - return $this->getContextNode()->getAttribute('source'); - } - - public function getBuildInformation(): BuildInformation - { - $buildNode = $this->getDom()->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'build' - )->item(0); - - if (!$buildNode) { - $buildNode = $this->getDom()->documentElement->appendChild( - $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'build' - ) - ); - } - - return new BuildInformation($buildNode); - } - - public function getTests(): Tests - { - $testsNode = $this->getContextNode()->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'tests' - )->item(0); - - if (!$testsNode) { - $testsNode = $this->getContextNode()->appendChild( - $this->getDom()->createElementNS( - 'https://schema.phpunit.de/coverage/1.0', - 'tests' - ) - ); - } - - return new Tests($testsNode); - } - - public function asDom(): \DOMDocument - { - return $this->getDom(); - } - - private function init(): void - { - $dom = new \DOMDocument; - $dom->loadXML(''); - - $this->setContextNode( - $dom->getElementsByTagNameNS( - 'https://schema.phpunit.de/coverage/1.0', - 'project' - )->item(0) - ); - } - - private function setProjectSourceDirectory(string $name): void - { - $this->getContextNode()->setAttribute('source', $name); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\Node\AbstractNode as Node; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; - -/** - * Renders a directory node. - */ -final class Directory extends Renderer -{ - /** - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public function render(DirectoryNode $node, string $file): void - { - $template = new \Text_Template($this->templatePath . 'directory.html', '{{', '}}'); - - $this->setCommonTemplateVariables($template, $node); - - $items = $this->renderItem($node, true); - - foreach ($node->getDirectories() as $item) { - $items .= $this->renderItem($item); - } - - foreach ($node->getFiles() as $item) { - $items .= $this->renderItem($item); - } - - $template->setVar( - [ - 'id' => $node->getId(), - 'items' => $items, - ] - ); - - $template->renderTo($file); - } - - protected function renderItem(Node $node, bool $total = false): string - { - $data = [ - 'numClasses' => $node->getNumClassesAndTraits(), - 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), - 'numMethods' => $node->getNumFunctionsAndMethods(), - 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), - 'linesExecutedPercent' => $node->getLineExecutedPercent(false), - 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), - 'numExecutedLines' => $node->getNumExecutedLines(), - 'numExecutableLines' => $node->getNumExecutableLines(), - 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), - 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), - 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), - 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), - ]; - - if ($total) { - $data['name'] = 'Total'; - } else { - if ($node instanceof DirectoryNode) { - $data['name'] = \sprintf( - '%s', - $node->getName(), - $node->getName() - ); - - $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); - - $data['icon'] = \sprintf('', $up); - } else { - $data['name'] = \sprintf( - '%s', - $node->getName(), - $node->getName() - ); - - $up = \str_repeat('../', \count($node->getPathAsArray()) - 2); - - $data['icon'] = \sprintf('', $up); - } - } - - return $this->renderItemTemplate( - new \Text_Template($this->templatePath . 'directory_item.html', '{{', '}}'), - $data - ); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\Node\AbstractNode; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; - -/** - * Renders the dashboard for a directory node. - */ -final class Dashboard extends Renderer -{ - /** - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public function render(DirectoryNode $node, string $file): void - { - $classes = $node->getClassesAndTraits(); - $template = new \Text_Template( - $this->templatePath . 'dashboard.html', - '{{', - '}}' - ); - - $this->setCommonTemplateVariables($template, $node); - - $baseLink = $node->getId() . '/'; - $complexity = $this->complexity($classes, $baseLink); - $coverageDistribution = $this->coverageDistribution($classes); - $insufficientCoverage = $this->insufficientCoverage($classes, $baseLink); - $projectRisks = $this->projectRisks($classes, $baseLink); - - $template->setVar( - [ - 'insufficient_coverage_classes' => $insufficientCoverage['class'], - 'insufficient_coverage_methods' => $insufficientCoverage['method'], - 'project_risks_classes' => $projectRisks['class'], - 'project_risks_methods' => $projectRisks['method'], - 'complexity_class' => $complexity['class'], - 'complexity_method' => $complexity['method'], - 'class_coverage_distribution' => $coverageDistribution['class'], - 'method_coverage_distribution' => $coverageDistribution['method'], - ] - ); - - $template->renderTo($file); - } - - /** - * Returns the data for the Class/Method Complexity charts. - */ - protected function complexity(array $classes, string $baseLink): array - { - $result = ['class' => [], 'method' => []]; - - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($className !== '*') { - $methodName = $className . '::' . $methodName; - } - - $result['method'][] = [ - $method['coverage'], - $method['ccn'], - \sprintf( - '%s', - \str_replace($baseLink, '', $method['link']), - $methodName - ), - ]; - } - - $result['class'][] = [ - $class['coverage'], - $class['ccn'], - \sprintf( - '%s', - \str_replace($baseLink, '', $class['link']), - $className - ), - ]; - } - - return [ - 'class' => \json_encode($result['class']), - 'method' => \json_encode($result['method']), - ]; - } - - /** - * Returns the data for the Class / Method Coverage Distribution chart. - */ - protected function coverageDistribution(array $classes): array - { - $result = [ - 'class' => [ - '0%' => 0, - '0-10%' => 0, - '10-20%' => 0, - '20-30%' => 0, - '30-40%' => 0, - '40-50%' => 0, - '50-60%' => 0, - '60-70%' => 0, - '70-80%' => 0, - '80-90%' => 0, - '90-100%' => 0, - '100%' => 0, - ], - 'method' => [ - '0%' => 0, - '0-10%' => 0, - '10-20%' => 0, - '20-30%' => 0, - '30-40%' => 0, - '40-50%' => 0, - '50-60%' => 0, - '60-70%' => 0, - '70-80%' => 0, - '80-90%' => 0, - '90-100%' => 0, - '100%' => 0, - ], - ]; - - foreach ($classes as $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] === 0) { - $result['method']['0%']++; - } elseif ($method['coverage'] === 100) { - $result['method']['100%']++; - } else { - $key = \floor($method['coverage'] / 10) * 10; - $key = $key . '-' . ($key + 10) . '%'; - $result['method'][$key]++; - } - } - - if ($class['coverage'] === 0) { - $result['class']['0%']++; - } elseif ($class['coverage'] === 100) { - $result['class']['100%']++; - } else { - $key = \floor($class['coverage'] / 10) * 10; - $key = $key . '-' . ($key + 10) . '%'; - $result['class'][$key]++; - } - } - - return [ - 'class' => \json_encode(\array_values($result['class'])), - 'method' => \json_encode(\array_values($result['method'])), - ]; - } - - /** - * Returns the classes / methods with insufficient coverage. - */ - protected function insufficientCoverage(array $classes, string $baseLink): array - { - $leastTestedClasses = []; - $leastTestedMethods = []; - $result = ['class' => '', 'method' => '']; - - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] < $this->highLowerBound) { - $key = $methodName; - - if ($className !== '*') { - $key = $className . '::' . $methodName; - } - - $leastTestedMethods[$key] = $method['coverage']; - } - } - - if ($class['coverage'] < $this->highLowerBound) { - $leastTestedClasses[$className] = $class['coverage']; - } - } - - \asort($leastTestedClasses); - \asort($leastTestedMethods); - - foreach ($leastTestedClasses as $className => $coverage) { - $result['class'] .= \sprintf( - ' %s%d%%' . "\n", - \str_replace($baseLink, '', $classes[$className]['link']), - $className, - $coverage - ); - } - - foreach ($leastTestedMethods as $methodName => $coverage) { - [$class, $method] = \explode('::', $methodName); - - $result['method'] .= \sprintf( - ' %s%d%%' . "\n", - \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), - $methodName, - $method, - $coverage - ); - } - - return $result; - } - - /** - * Returns the project risks according to the CRAP index. - */ - protected function projectRisks(array $classes, string $baseLink): array - { - $classRisks = []; - $methodRisks = []; - $result = ['class' => '', 'method' => '']; - - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - if ($method['coverage'] < $this->highLowerBound && $method['ccn'] > 1) { - $key = $methodName; - - if ($className !== '*') { - $key = $className . '::' . $methodName; - } - - $methodRisks[$key] = $method['crap']; - } - } - - if ($class['coverage'] < $this->highLowerBound && - $class['ccn'] > \count($class['methods'])) { - $classRisks[$className] = $class['crap']; - } - } - - \arsort($classRisks); - \arsort($methodRisks); - - foreach ($classRisks as $className => $crap) { - $result['class'] .= \sprintf( - ' %s%d' . "\n", - \str_replace($baseLink, '', $classes[$className]['link']), - $className, - $crap - ); - } - - foreach ($methodRisks as $methodName => $crap) { - [$class, $method] = \explode('::', $methodName); - - $result['method'] .= \sprintf( - ' %s%d' . "\n", - \str_replace($baseLink, '', $classes[$class]['methods'][$method]['link']), - $methodName, - $method, - $crap - ); - } - - return $result; - } - - protected function getActiveBreadcrumb(AbstractNode $node): string - { - return \sprintf( - ' ' . "\n" . - ' ' . "\n", - $node->getName() - ); - } -} -/* - Copyright (C) Federico Zivolo 2018 - Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT). - */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=J(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=Z,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=$(J(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Q(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=z(n);break;case he.COUNTERCLOCKWISE:p=z(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=Z,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=G(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,C(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=D(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right offset) { - $top_link.fadeIn(); - } else { - $top_link.fadeOut(); - } - }).scroll(); - - $('.popin') - .popover({trigger: 'manual'}) - .on({ - 'mouseenter.popover': function () { - var $target = $(this); - - $target.data('popover-hover', true); - - // popover already displayed - if ($target.next('.popover').length) { - return; - } - - // show the popover - $target.popover('show'); - - // register mouse events on the popover - $target.next('.popover:not(.popover-initialized)') - .on({ - 'mouseenter': function () { - $target.data('popover-hover', true); - }, - 'mouseleave': function () { - hidePopover($target); - } - }) - .addClass('popover-initialized'); - }, - 'mouseleave.popover': function () { - hidePopover($(this)); - } - }); - }); -/* nvd3 version 1.8.1 (https://github.com/novus/nvd3) 2015-06-15 */ -!function(){var a={};a.dev=!1,a.tooltip=a.tooltip||{},a.utils=a.utils||{},a.models=a.models||{},a.charts={},a.logs={},a.dom={},a.dispatch=d3.dispatch("render_start","render_end"),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d&&a?this:a,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),a.dev&&(a.dispatch.on("render_start",function(){a.logs.startTime=+new Date}),a.dispatch.on("render_end",function(){a.logs.endTime=+new Date,a.logs.totalTime=a.logs.endTime-a.logs.startTime,a.log("total",a.logs.totalTime)})),a.log=function(){if(a.dev&&window.console&&console.log&&console.log.apply)console.log.apply(console,arguments);else if(a.dev&&window.console&&"function"==typeof console.log&&Function.prototype.bind){var b=Function.prototype.bind.call(console.log,console);b.apply(console,arguments)}return arguments[arguments.length-1]},a.deprecated=function(a,b){console&&console.warn&&console.warn("nvd3 warning: `"+a+"` has been deprecated. ",b||"")},a.render=function(b){b=b||1,a.render.active=!0,a.dispatch.render_start();var c=function(){for(var d,e,f=0;b>f&&(e=a.render.queue[f]);f++)d=e.generate(),typeof e.callback==typeof Function&&e.callback(d);a.render.queue.splice(0,f),a.render.queue.length?setTimeout(c):(a.dispatch.render_end(),a.render.active=!1)};setTimeout(c)},a.render.active=!1,a.render.queue=[],a.addGraph=function(b){typeof arguments[0]==typeof Function&&(b={generate:arguments[0],callback:arguments[1]}),a.render.queue.push(b),a.render.active||a.render()},"undefined"!=typeof module&&"undefined"!=typeof exports&&(module.exports=a),"undefined"!=typeof window&&(window.nv=a),a.dom.write=function(a){return void 0!==window.fastdom?fastdom.write(a):a()},a.dom.read=function(a){return void 0!==window.fastdom?fastdom.read(a):a()},a.interactiveGuideline=function(){"use strict";function b(l){l.each(function(l){function m(){var a=d3.mouse(this),d=a[0],e=a[1],i=!0,j=!1;if(k&&(d=d3.event.offsetX,e=d3.event.offsetY,"svg"!==d3.event.target.tagName&&(i=!1),d3.event.target.className.baseVal.match("nv-legend")&&(j=!0)),i&&(d-=f.left,e-=f.top),0>d||0>e||d>o||e>p||d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement||j){if(k&&d3.event.relatedTarget&&void 0===d3.event.relatedTarget.ownerSVGElement&&(void 0===d3.event.relatedTarget.className||d3.event.relatedTarget.className.match(c.nvPointerEventsClass)))return;return h.elementMouseout({mouseX:d,mouseY:e}),b.renderGuideLine(null),void c.hidden(!0)}c.hidden(!1);var l=g.invert(d);h.elementMousemove({mouseX:d,mouseY:e,pointXValue:l}),"dblclick"===d3.event.type&&h.elementDblclick({mouseX:d,mouseY:e,pointXValue:l}),"click"===d3.event.type&&h.elementClick({mouseX:d,mouseY:e,pointXValue:l})}var n=d3.select(this),o=d||960,p=e||400,q=n.selectAll("g.nv-wrap.nv-interactiveLineLayer").data([l]),r=q.enter().append("g").attr("class"," nv-wrap nv-interactiveLineLayer");r.append("g").attr("class","nv-interactiveGuideLine"),j&&(j.on("touchmove",m).on("mousemove",m,!0).on("mouseout",m,!0).on("dblclick",m).on("click",m),b.guideLine=null,b.renderGuideLine=function(c){i&&(b.guideLine&&b.guideLine.attr("x1")===c||a.dom.write(function(){var b=q.select(".nv-interactiveGuideLine").selectAll("line").data(null!=c?[a.utils.NaNtoZero(c)]:[],String);b.enter().append("line").attr("class","nv-guideline").attr("x1",function(a){return a}).attr("x2",function(a){return a}).attr("y1",p).attr("y2",0),b.exit().remove()}))})})}var c=a.models.tooltip();c.duration(0).hideDelay(0)._isInteractiveLayer(!0).hidden(!1);var d=null,e=null,f={left:0,top:0},g=d3.scale.linear(),h=d3.dispatch("elementMousemove","elementMouseout","elementClick","elementDblclick"),i=!0,j=null,k="ActiveXObject"in window;return b.dispatch=h,b.tooltip=c,b.margin=function(a){return arguments.length?(f.top="undefined"!=typeof a.top?a.top:f.top,f.left="undefined"!=typeof a.left?a.left:f.left,b):f},b.width=function(a){return arguments.length?(d=a,b):d},b.height=function(a){return arguments.length?(e=a,b):e},b.xScale=function(a){return arguments.length?(g=a,b):g},b.showGuideLine=function(a){return arguments.length?(i=a,b):i},b.svgContainer=function(a){return arguments.length?(j=a,b):j},b},a.interactiveBisect=function(a,b,c){"use strict";if(!(a instanceof Array))return null;var d;d="function"!=typeof c?function(a){return a.x}:c;var e=function(a,b){return d(a)-b},f=d3.bisector(e).left,g=d3.max([0,f(a,b)-1]),h=d(a[g]);if("undefined"==typeof h&&(h=g),h===b)return g;var i=d3.min([g+1,a.length-1]),j=d(a[i]);return"undefined"==typeof j&&(j=i),Math.abs(j-b)>=Math.abs(h-b)?g:i},a.nearestValueIndex=function(a,b,c){"use strict";var d=1/0,e=null;return a.forEach(function(a,f){var g=Math.abs(b-a);null!=a&&d>=g&&c>g&&(d=g,e=f)}),e},function(){"use strict";a.models.tooltip=function(){function b(){if(k){var a=d3.select(k);"svg"!==a.node().tagName&&(a=a.select("svg"));var b=a.node()?a.attr("viewBox"):null;if(b){b=b.split(" ");var c=parseInt(a.style("width"),10)/b[2];p.left=p.left*c,p.top=p.top*c}}}function c(){if(!n){var a;a=k?k:document.body,n=d3.select(a).append("div").attr("class","nvtooltip "+(j?j:"xy-tooltip")).attr("id",v),n.style("top",0).style("left",0),n.style("opacity",0),n.selectAll("div, table, td, tr").classed(w,!0),n.classed(w,!0),o=n.node()}}function d(){if(r&&B(e)){b();var f=p.left,g=null!==i?i:p.top;return a.dom.write(function(){c();var b=A(e);b&&(o.innerHTML=b),k&&u?a.dom.read(function(){var a=k.getElementsByTagName("svg")[0],b={left:0,top:0};if(a){var c=a.getBoundingClientRect(),d=k.getBoundingClientRect(),e=c.top;if(0>e){var i=k.getBoundingClientRect();e=Math.abs(e)>i.height?0:e}b.top=Math.abs(e-d.top),b.left=Math.abs(c.left-d.left)}f+=k.offsetLeft+b.left-2*k.scrollLeft,g+=k.offsetTop+b.top-2*k.scrollTop,h&&h>0&&(g=Math.floor(g/h)*h),C([f,g])}):C([f,g])}),d}}var e=null,f="w",g=25,h=0,i=null,j=null,k=null,l=!0,m=400,n=null,o=null,p={left:null,top:null},q={left:0,top:0},r=!0,s=100,t=!0,u=!1,v="nvtooltip-"+Math.floor(1e5*Math.random()),w="nv-pointer-events-none",x=function(a){return a},y=function(a){return a},z=function(a){return a},A=function(a){if(null===a)return"";var b=d3.select(document.createElement("table"));if(t){var c=b.selectAll("thead").data([a]).enter().append("thead");c.append("tr").append("td").attr("colspan",3).append("strong").classed("x-value",!0).html(y(a.value))}var d=b.selectAll("tbody").data([a]).enter().append("tbody"),e=d.selectAll("tr").data(function(a){return a.series}).enter().append("tr").classed("highlight",function(a){return a.highlight});e.append("td").classed("legend-color-guide",!0).append("div").style("background-color",function(a){return a.color}),e.append("td").classed("key",!0).html(function(a,b){return z(a.key,b)}),e.append("td").classed("value",!0).html(function(a,b){return x(a.value,b)}),e.selectAll("td").each(function(a){if(a.highlight){var b=d3.scale.linear().domain([0,1]).range(["#fff",a.color]),c=.6;d3.select(this).style("border-bottom-color",b(c)).style("border-top-color",b(c))}});var f=b.node().outerHTML;return void 0!==a.footer&&(f+=""),f},B=function(a){if(a&&a.series){if(a.series instanceof Array)return!!a.series.length;if(a.series instanceof Object)return a.series=[a.series],!0}return!1},C=function(b){o&&a.dom.read(function(){var c,d,e=parseInt(o.offsetHeight,10),h=parseInt(o.offsetWidth,10),i=a.utils.windowSize().width,j=a.utils.windowSize().height,k=window.pageYOffset,p=window.pageXOffset;j=window.innerWidth>=document.body.scrollWidth?j:j-16,i=window.innerHeight>=document.body.scrollHeight?i:i-16;var r,t,u=function(a){var b=d;do isNaN(a.offsetTop)||(b+=a.offsetTop),a=a.offsetParent;while(a);return b},v=function(a){var b=c;do isNaN(a.offsetLeft)||(b+=a.offsetLeft),a=a.offsetParent;while(a);return b};switch(f){case"e":c=b[0]-h-g,d=b[1]-e/2,r=v(o),t=u(o),p>r&&(c=b[0]+g>p?b[0]+g:p-r+c),k>t&&(d=k-t+d),t+e>k+j&&(d=k+j-t+d-e);break;case"w":c=b[0]+g,d=b[1]-e/2,r=v(o),t=u(o),r+h>i&&(c=b[0]-h-g),k>t&&(d=k+5),t+e>k+j&&(d=k+j-t+d-e);break;case"n":c=b[0]-h/2-5,d=b[1]+g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),t+e>k+j&&(d=k+j-t+d-e);break;case"s":c=b[0]-h/2,d=b[1]-e-g,r=v(o),t=u(o),p>r&&(c=p+5),r+h>i&&(c=c-h/2+5),k>t&&(d=k);break;case"none":c=b[0],d=b[1]-g,r=v(o),t=u(o)}c-=q.left,d-=q.top;var w=o.getBoundingClientRect(),k=window.pageYOffset||document.documentElement.scrollTop,p=window.pageXOffset||document.documentElement.scrollLeft,x="translate("+(w.left+p)+"px, "+(w.top+k)+"px)",y="translate("+c+"px, "+d+"px)",z=d3.interpolateString(x,y),A=n.style("opacity")<.1;l?n.transition().delay(m).duration(0).style("opacity",0):n.interrupt().transition().duration(A?0:s).styleTween("transform",function(){return z},"important").style("-webkit-transform",y).style("opacity",1)})};return d.nvPointerEventsClass=w,d.options=a.utils.optionsFunc.bind(d),d._options=Object.create({},{duration:{get:function(){return s},set:function(a){s=a}},gravity:{get:function(){return f},set:function(a){f=a}},distance:{get:function(){return g},set:function(a){g=a}},snapDistance:{get:function(){return h},set:function(a){h=a}},classes:{get:function(){return j},set:function(a){j=a}},chartContainer:{get:function(){return k},set:function(a){k=a}},fixedTop:{get:function(){return i},set:function(a){i=a}},enabled:{get:function(){return r},set:function(a){r=a}},hideDelay:{get:function(){return m},set:function(a){m=a}},contentGenerator:{get:function(){return A},set:function(a){A=a}},valueFormatter:{get:function(){return x},set:function(a){x=a}},headerFormatter:{get:function(){return y},set:function(a){y=a}},keyFormatter:{get:function(){return z},set:function(a){z=a}},headerEnabled:{get:function(){return t},set:function(a){t=a}},_isInteractiveLayer:{get:function(){return u},set:function(a){u=!!a}},position:{get:function(){return p},set:function(a){p.left=void 0!==a.left?a.left:p.left,p.top=void 0!==a.top?a.top:p.top}},offset:{get:function(){return q},set:function(a){q.left=void 0!==a.left?a.left:q.left,q.top=void 0!==a.top?a.top:q.top}},hidden:{get:function(){return l},set:function(a){l!=a&&(l=!!a,d())}},data:{get:function(){return e},set:function(a){a.point&&(a.value=a.point.x,a.series=a.series||{},a.series.value=a.point.y,a.series.color=a.point.color||a.series.color),e=a}},tooltipElem:{get:function(){return o},set:function(){}},id:{get:function(){return v},set:function(){}}}),a.utils.initOptions(d),d}}(),a.utils.windowSize=function(){var a={width:640,height:480};return window.innerWidth&&window.innerHeight?(a.width=window.innerWidth,a.height=window.innerHeight,a):"CSS1Compat"==document.compatMode&&document.documentElement&&document.documentElement.offsetWidth?(a.width=document.documentElement.offsetWidth,a.height=document.documentElement.offsetHeight,a):document.body&&document.body.offsetWidth?(a.width=document.body.offsetWidth,a.height=document.body.offsetHeight,a):a},a.utils.windowResize=function(b){return window.addEventListener?window.addEventListener("resize",b):a.log("ERROR: Failed to bind to window.resize with: ",b),{callback:b,clear:function(){window.removeEventListener("resize",b)}}},a.utils.getColor=function(b){if(void 0===b)return a.utils.defaultColor();if(Array.isArray(b)){var c=d3.scale.ordinal().range(b);return function(a,b){var d=void 0===b?a:b;return a.color||c(d)}}return b},a.utils.defaultColor=function(){return a.utils.getColor(d3.scale.category20().range())},a.utils.customTheme=function(a,b,c){b=b||function(a){return a.key},c=c||d3.scale.category20().range();var d=c.length;return function(e){var f=b(e);return"function"==typeof a[f]?a[f]():void 0!==a[f]?a[f]:(d||(d=c.length),d-=1,c[d])}},a.utils.pjax=function(b,c){var d=function(d){d3.html(d,function(d){var e=d3.select(c).node();e.parentNode.replaceChild(d3.select(d).select(c).node(),e),a.utils.pjax(b,c)})};d3.selectAll(b).on("click",function(){history.pushState(this.href,this.textContent,this.href),d(this.href),d3.event.preventDefault()}),d3.select(window).on("popstate",function(){d3.event.state&&d(d3.event.state)})},a.utils.calcApproxTextWidth=function(a){if("function"==typeof a.style&&"function"==typeof a.text){var b=parseInt(a.style("font-size").replace("px",""),10),c=a.text().length;return c*b*.5}return 0},a.utils.NaNtoZero=function(a){return"number"!=typeof a||isNaN(a)||null===a||1/0===a||a===-1/0?0:a},d3.selection.prototype.watchTransition=function(a){var b=[this].concat([].slice.call(arguments,1));return a.transition.apply(a,b)},a.utils.renderWatch=function(b,c){if(!(this instanceof a.utils.renderWatch))return new a.utils.renderWatch(b,c);var d=void 0!==c?c:250,e=[],f=this;this.models=function(a){return a=[].slice.call(arguments,0),a.forEach(function(a){a.__rendered=!1,function(a){a.dispatch.on("renderEnd",function(){a.__rendered=!0,f.renderEnd("model")})}(a),e.indexOf(a)<0&&e.push(a)}),this},this.reset=function(a){void 0!==a&&(d=a),e=[]},this.transition=function(a,b,c){if(b=arguments.length>1?[].slice.call(arguments,1):[],c=b.length>1?b.pop():void 0!==d?d:250,a.__rendered=!1,e.indexOf(a)<0&&e.push(a),0===c)return a.__rendered=!0,a.delay=function(){return this},a.duration=function(){return this},a;a.__rendered=0===a.length?!0:a.every(function(a){return!a.length})?!0:!1;var g=0;return a.transition().duration(c).each(function(){++g}).each("end",function(){0===--g&&(a.__rendered=!0,f.renderEnd.apply(this,b))})},this.renderEnd=function(){e.every(function(a){return a.__rendered})&&(e.forEach(function(a){a.__rendered=!1}),b.renderEnd.apply(this,arguments))}},a.utils.deepExtend=function(b){var c=arguments.length>1?[].slice.call(arguments,1):[];c.forEach(function(c){for(var d in c){var e=b[d]instanceof Array,f="object"==typeof b[d],g="object"==typeof c[d];f&&!e&&g?a.utils.deepExtend(b[d],c[d]):b[d]=c[d]}})},a.utils.state=function(){if(!(this instanceof a.utils.state))return new a.utils.state;var b={},c=function(){},d=function(){return{}},e=null,f=null;this.dispatch=d3.dispatch("change","set"),this.dispatch.on("set",function(a){c(a,!0)}),this.getter=function(a){return d=a,this},this.setter=function(a,b){return b||(b=function(){}),c=function(c,d){a(c),d&&b()},this},this.init=function(b){e=e||{},a.utils.deepExtend(e,b)};var g=function(){var a=d();if(JSON.stringify(a)===JSON.stringify(b))return!1;for(var c in a)void 0===b[c]&&(b[c]={}),b[c]=a[c],f=!0;return!0};this.update=function(){e&&(c(e,!1),e=null),g.call(this)&&this.dispatch.change(b)}},a.utils.optionsFunc=function(a){return a&&d3.map(a).forEach(function(a,b){"function"==typeof this[a]&&this[a](b)}.bind(this)),this},a.utils.calcTicksX=function(b,c){var d=1,e=0;for(e;ed?f:d}return a.log("Requested number of ticks: ",b),a.log("Calculated max values to be: ",d),b=b>d?b=d-1:b,b=1>b?1:b,b=Math.floor(b),a.log("Calculating tick count as: ",b),b},a.utils.calcTicksY=function(b,c){return a.utils.calcTicksX(b,c)},a.utils.initOption=function(a,b){a._calls&&a._calls[b]?a[b]=a._calls[b]:(a[b]=function(c){return arguments.length?(a._overrides[b]=!0,a._options[b]=c,a):a._options[b]},a["_"+b]=function(c){return arguments.length?(a._overrides[b]||(a._options[b]=c),a):a._options[b]})},a.utils.initOptions=function(b){b._overrides=b._overrides||{};var c=Object.getOwnPropertyNames(b._options||{}),d=Object.getOwnPropertyNames(b._calls||{});c=c.concat(d);for(var e in c)a.utils.initOption(b,c[e])},a.utils.inheritOptionsD3=function(a,b,c){a._d3options=c.concat(a._d3options||[]),c.unshift(b),c.unshift(a),d3.rebind.apply(this,c)},a.utils.arrayUnique=function(a){return a.sort().filter(function(b,c){return!c||b!=a[c-1]})},a.utils.symbolMap=d3.map(),a.utils.symbol=function(){function b(b,e){var f=c.call(this,b,e),g=d.call(this,b,e);return-1!==d3.svg.symbolTypes.indexOf(f)?d3.svg.symbol().type(f).size(g)():a.utils.symbolMap.get(f)(g)}var c,d=64;return b.type=function(a){return arguments.length?(c=d3.functor(a),b):c},b.size=function(a){return arguments.length?(d=d3.functor(a),b):d},b},a.utils.inheritOptions=function(b,c){var d=Object.getOwnPropertyNames(c._options||{}),e=Object.getOwnPropertyNames(c._calls||{}),f=c._inherited||[],g=c._d3options||[],h=d.concat(e).concat(f).concat(g);h.unshift(c),h.unshift(b),d3.rebind.apply(this,h),b._inherited=a.utils.arrayUnique(d.concat(e).concat(f).concat(d).concat(b._inherited||[])),b._d3options=a.utils.arrayUnique(g.concat(b._d3options||[]))},a.utils.initSVG=function(a){a.classed({"nvd3-svg":!0})},a.utils.sanitizeHeight=function(a,b){return a||parseInt(b.style("height"),10)||400},a.utils.sanitizeWidth=function(a,b){return a||parseInt(b.style("width"),10)||960},a.utils.availableHeight=function(b,c,d){return a.utils.sanitizeHeight(b,c)-d.top-d.bottom},a.utils.availableWidth=function(b,c,d){return a.utils.sanitizeWidth(b,c)-d.left-d.right},a.utils.noData=function(b,c){var d=b.options(),e=d.margin(),f=d.noData(),g=null==f?["No Data Available."]:[f],h=a.utils.availableHeight(d.height(),c,e),i=a.utils.availableWidth(d.width(),c,e),j=e.left+i/2,k=e.top+h/2;c.selectAll("g").remove();var l=c.selectAll(".nv-noData").data(g);l.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),l.attr("x",j).attr("y",k).text(function(a){return a})},a.models.axis=function(){"use strict";function b(g){return s.reset(),g.each(function(b){var g=d3.select(this);a.utils.initSVG(g);var p=g.selectAll("g.nv-wrap.nv-axis").data([b]),q=p.enter().append("g").attr("class","nvd3 nv-wrap nv-axis"),t=(q.append("g"),p.select("g"));null!==n?c.ticks(n):("top"==c.orient()||"bottom"==c.orient())&&c.ticks(Math.abs(d.range()[1]-d.range()[0])/100),t.watchTransition(s,"axis").call(c),r=r||c.scale();var u=c.tickFormat();null==u&&(u=r.tickFormat());var v=t.selectAll("text.nv-axislabel").data([h||null]);v.exit().remove();var w,x,y;switch(c.orient()){case"top":v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",0).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b))+",0)"}).select("text").attr("dy","-0.5em").attr("y",-c.tickPadding()).attr("text-anchor","middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max top").attr("transform",function(b,c){return"translate("+a.utils.NaNtoZero(d.range()[c])+",0)"}));break;case"bottom":w=o+36;var z=30,A=0,B=t.selectAll("g").select("text"),C="";if(j%360){B.each(function(){var a=this.getBoundingClientRect(),b=a.width;A=a.height,b>z&&(z=b)}),C="rotate("+j+" 0,"+(A/2+c.tickPadding())+")";var D=Math.abs(Math.sin(j*Math.PI/180));w=(D?D*z:z)+30,B.attr("transform",C).style("text-anchor",j%360>0?"start":"end")}v.enter().append("text").attr("class","nv-axislabel"),y=d.range().length<2?0:2===d.range().length?d.range()[1]:d.range()[d.range().length-1]+(d.range()[1]-d.range()[0]),v.attr("text-anchor","middle").attr("y",w).attr("x",y/2),i&&(x=p.selectAll("g.nv-axisMaxMin").data([d.domain()[0],d.domain()[d.domain().length-1]]),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-x",0==b?"nv-axisMin-x":"nv-axisMax-x"].join(" ")}).append("text"),x.exit().remove(),x.attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"}).select("text").attr("dy",".71em").attr("y",c.tickPadding()).attr("transform",C).style("text-anchor",j?j%360>0?"start":"end":"middle").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max bottom").attr("transform",function(b){return"translate("+a.utils.NaNtoZero(d(b)+(m?d.rangeBand()/2:0))+",0)"})),l&&B.attr("transform",function(a,b){return"translate(0,"+(b%2==0?"0":"12")+")"});break;case"right":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"begin").attr("transform",k?"rotate(90)":"").attr("y",k?-Math.max(e.right,f)+12:-10).attr("x",k?d3.max(d.range())/2:c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(d(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",c.tickPadding()).style("text-anchor","start").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1));break;case"left":v.enter().append("text").attr("class","nv-axislabel"),v.style("text-anchor",k?"middle":"end").attr("transform",k?"rotate(-90)":"").attr("y",k?-Math.max(e.left,f)+25-(o||0):-10).attr("x",k?-d3.max(d.range())/2:-c.tickPadding()),i&&(x=p.selectAll("g.nv-axisMaxMin").data(d.domain()),x.enter().append("g").attr("class",function(a,b){return["nv-axisMaxMin","nv-axisMaxMin-y",0==b?"nv-axisMin-y":"nv-axisMax-y"].join(" ")}).append("text").style("opacity",0),x.exit().remove(),x.attr("transform",function(b){return"translate(0,"+a.utils.NaNtoZero(r(b))+")"}).select("text").attr("dy",".32em").attr("y",0).attr("x",-c.tickPadding()).attr("text-anchor","end").text(function(a){var b=u(a);return(""+b).match("NaN")?"":b}),x.watchTransition(s,"min-max right").attr("transform",function(b,c){return"translate(0,"+a.utils.NaNtoZero(d.range()[c])+")"}).select("text").style("opacity",1))}if(v.text(function(a){return a}),!i||"left"!==c.orient()&&"right"!==c.orient()||(t.selectAll("g").each(function(a){d3.select(this).select("text").attr("opacity",1),(d(a)d.range()[0]-10)&&((a>1e-10||-1e-10>a)&&d3.select(this).attr("opacity",0),d3.select(this).select("text").attr("opacity",0))}),d.domain()[0]==d.domain()[1]&&0==d.domain()[0]&&p.selectAll("g.nv-axisMaxMin").style("opacity",function(a,b){return b?0:1})),i&&("top"===c.orient()||"bottom"===c.orient())){var E=[];p.selectAll("g.nv-axisMaxMin").each(function(a,b){try{E.push(b?d(a)-this.getBoundingClientRect().width-4:d(a)+this.getBoundingClientRect().width+4)}catch(c){E.push(b?d(a)-4:d(a)+4)}}),t.selectAll("g").each(function(a){(d(a)E[1])&&(a>1e-10||-1e-10>a?d3.select(this).remove():d3.select(this).select("text").remove())})}t.selectAll(".tick").filter(function(a){return!parseFloat(Math.round(1e5*a)/1e6)&&void 0!==a}).classed("zero",!0),r=d.copy()}),s.renderEnd("axis immediate"),b}var c=d3.svg.axis(),d=d3.scale.linear(),e={top:0,right:0,bottom:0,left:0},f=75,g=60,h=null,i=!0,j=0,k=!0,l=!1,m=!1,n=null,o=0,p=250,q=d3.dispatch("renderEnd");c.scale(d).orient("bottom").tickFormat(function(a){return a});var r,s=a.utils.renderWatch(q,p);return b.axis=c,b.dispatch=q,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{axisLabelDistance:{get:function(){return o},set:function(a){o=a}},staggerLabels:{get:function(){return l},set:function(a){l=a}},rotateLabels:{get:function(){return j},set:function(a){j=a}},rotateYLabel:{get:function(){return k},set:function(a){k=a}},showMaxMin:{get:function(){return i},set:function(a){i=a}},axisLabel:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return g},set:function(a){g=a}},ticks:{get:function(){return n},set:function(a){n=a}},width:{get:function(){return f},set:function(a){f=a}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},duration:{get:function(){return p},set:function(a){p=a,s.reset(p)}},scale:{get:function(){return d},set:function(e){d=e,c.scale(d),m="function"==typeof d.rangeBands,a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"])}}}),a.utils.initOptions(b),a.utils.inheritOptionsD3(b,c,["orient","tickValues","tickSubdivide","tickSize","tickPadding","tickFormat"]),a.utils.inheritOptionsD3(b,d,["domain","range","rangeBand","rangeBands"]),b},a.models.boxPlot=function(){"use strict";function b(l){return v.reset(),l.each(function(b){var l=j-i.left-i.right,p=k-i.top-i.bottom;r=d3.select(this),a.utils.initSVG(r),m.domain(c||b.map(function(a,b){return o(a,b)})).rangeBands(e||[0,l],.1);var w=[];if(!d){var x=d3.min(b.map(function(a){var b=[];return b.push(a.values.Q1),a.values.hasOwnProperty("whisker_low")&&null!==a.values.whisker_low&&b.push(a.values.whisker_low),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.min(b)})),y=d3.max(b.map(function(a){var b=[];return b.push(a.values.Q3),a.values.hasOwnProperty("whisker_high")&&null!==a.values.whisker_high&&b.push(a.values.whisker_high),a.values.hasOwnProperty("outliers")&&null!==a.values.outliers&&(b=b.concat(a.values.outliers)),d3.max(b)}));w=[x,y]}n.domain(d||w),n.range(f||[p,0]),g=g||m,h=h||n.copy().range([n(0),n(0)]);{var z=r.selectAll("g.nv-wrap").data([b]);z.enter().append("g").attr("class","nvd3 nv-wrap")}z.attr("transform","translate("+i.left+","+i.top+")");var A=z.selectAll(".nv-boxplot").data(function(a){return a}),B=A.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);A.attr("class","nv-boxplot").attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}).classed("hover",function(a){return a.hover}),A.watchTransition(v,"nv-boxplot: boxplots").style("stroke-opacity",1).style("fill-opacity",.75).delay(function(a,c){return c*t/b.length}).attr("transform",function(a,b){return"translate("+(m(o(a,b))+.05*m.rangeBand())+", 0)"}),A.exit().remove(),B.each(function(a,b){var c=d3.select(this);["low","high"].forEach(function(d){a.values.hasOwnProperty("whisker_"+d)&&null!==a.values["whisker_"+d]&&(c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-whisker nv-boxplot-"+d),c.append("line").style("stroke",a.color?a.color:q(a,b)).attr("class","nv-boxplot-tick nv-boxplot-"+d))})});var C=A.selectAll(".nv-boxplot-outlier").data(function(a){return a.values.hasOwnProperty("outliers")&&null!==a.values.outliers?a.values.outliers:[]});C.enter().append("circle").style("fill",function(a,b,c){return q(a,c)}).style("stroke",function(a,b,c){return q(a,c)}).on("mouseover",function(a,b,c){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:a,color:q(a,c)},e:d3.event})}).on("mouseout",function(a,b,c){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:a,color:q(a,c)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),C.attr("class","nv-boxplot-outlier"),C.watchTransition(v,"nv-boxplot: nv-boxplot-outlier").attr("cx",.45*m.rangeBand()).attr("cy",function(a){return n(a)}).attr("r","3"),C.exit().remove();var D=function(){return null===u?.9*m.rangeBand():Math.min(75,.9*m.rangeBand())},E=function(){return.45*m.rangeBand()-D()/2},F=function(){return.45*m.rangeBand()+D()/2};["low","high"].forEach(function(a){var b="low"===a?"Q1":"Q3";A.select("line.nv-boxplot-whisker.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",.45*m.rangeBand()).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",.45*m.rangeBand()).attr("y2",function(a){return n(a.values[b])}),A.select("line.nv-boxplot-tick.nv-boxplot-"+a).watchTransition(v,"nv-boxplot: boxplots").attr("x1",E).attr("y1",function(b){return n(b.values["whisker_"+a])}).attr("x2",F).attr("y2",function(b){return n(b.values["whisker_"+a])})}),["low","high"].forEach(function(a){B.selectAll(".nv-boxplot-"+a).on("mouseover",function(b,c,d){d3.select(this).classed("hover",!0),s.elementMouseover({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mouseout",function(b,c,d){d3.select(this).classed("hover",!1),s.elementMouseout({series:{key:b.values["whisker_"+a],color:q(b,d)},e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})})}),B.append("rect").attr("class","nv-boxplot-box").on("mouseover",function(a,b){d3.select(this).classed("hover",!0),s.elementMouseover({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),s.elementMouseout({key:a.label,value:a.label,series:[{key:"Q3",value:a.values.Q3,color:a.color||q(a,b)},{key:"Q2",value:a.values.Q2,color:a.color||q(a,b)},{key:"Q1",value:a.values.Q1,color:a.color||q(a,b)}],data:a,index:b,e:d3.event})}).on("mousemove",function(){s.elementMousemove({e:d3.event})}),A.select("rect.nv-boxplot-box").watchTransition(v,"nv-boxplot: boxes").attr("y",function(a){return n(a.values.Q3)}).attr("width",D).attr("x",E).attr("height",function(a){return Math.abs(n(a.values.Q3)-n(a.values.Q1))||1}).style("fill",function(a,b){return a.color||q(a,b)}).style("stroke",function(a,b){return a.color||q(a,b)}),B.append("line").attr("class","nv-boxplot-median"),A.select("line.nv-boxplot-median").watchTransition(v,"nv-boxplot: boxplots line").attr("x1",E).attr("y1",function(a){return n(a.values.Q2)}).attr("x2",F).attr("y2",function(a){return n(a.values.Q2)}),g=m.copy(),h=n.copy()}),v.renderEnd("nv-boxplot immediate"),b}var c,d,e,f,g,h,i={top:0,right:0,bottom:0,left:0},j=960,k=500,l=Math.floor(1e4*Math.random()),m=d3.scale.ordinal(),n=d3.scale.linear(),o=function(a){return a.x},p=function(a){return a.y},q=a.utils.defaultColor(),r=null,s=d3.dispatch("elementMouseover","elementMouseout","elementMousemove","renderEnd"),t=250,u=null,v=a.utils.renderWatch(s,t);return b.dispatch=s,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},maxBoxWidth:{get:function(){return u},set:function(a){u=a}},x:{get:function(){return o},set:function(a){o=a}},y:{get:function(){return p},set:function(a){p=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return l},set:function(a){l=a}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}},duration:{get:function(){return t},set:function(a){t=a,v.reset(t)}}}),a.utils.initOptions(b),b},a.models.boxPlotChart=function(){"use strict";function b(k){return t.reset(),t.models(e),l&&t.models(f),m&&t.models(g),k.each(function(k){var p=d3.select(this);a.utils.initSVG(p);var t=(i||parseInt(p.style("width"))||960)-h.left-h.right,u=(j||parseInt(p.style("height"))||400)-h.top-h.bottom;if(b.update=function(){r.beforeUpdate(),p.transition().duration(s).call(b)},b.container=this,!(k&&k.length&&k.filter(function(a){return a.values.hasOwnProperty("Q1")&&a.values.hasOwnProperty("Q2")&&a.values.hasOwnProperty("Q3")}).length)){var v=p.selectAll(".nv-noData").data([q]);return v.enter().append("text").attr("class","nvd3 nv-noData").attr("dy","-.7em").style("text-anchor","middle"),v.attr("x",h.left+t/2).attr("y",h.top+u/2).text(function(a){return a}),b}p.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var w=p.selectAll("g.nv-wrap.nv-boxPlotWithAxes").data([k]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-boxPlotWithAxes").append("g"),y=x.append("defs"),z=w.select("g"); -x.append("g").attr("class","nv-x nv-axis"),x.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),x.append("g").attr("class","nv-barsWrap"),z.attr("transform","translate("+h.left+","+h.top+")"),n&&z.select(".nv-y.nv-axis").attr("transform","translate("+t+",0)"),e.width(t).height(u);var A=z.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));if(A.transition().call(e),y.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),z.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(o?2:1)).attr("height",16).attr("x",-c.rangeBand()/(o?1:2)),l){f.scale(c).ticks(a.utils.calcTicksX(t/100,k)).tickSize(-u,0),z.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),z.select(".nv-x.nv-axis").call(f);var B=z.select(".nv-x.nv-axis").selectAll("g");o&&B.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}m&&(g.scale(d).ticks(Math.floor(u/36)).tickSize(-t,0),z.select(".nv-y.nv-axis").call(g)),z.select(".nv-zeroLine line").attr("x1",0).attr("x2",t).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("nv-boxplot chart immediate"),b}var c,d,e=a.models.boxPlot(),f=a.models.axis(),g=a.models.axis(),h={top:15,right:10,bottom:50,left:60},i=null,j=null,k=a.utils.getColor(),l=!0,m=!0,n=!1,o=!1,p=a.models.tooltip(),q="No Data Available.",r=d3.dispatch("tooltipShow","tooltipHide","beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(n?"right":"left").tickFormat(d3.format(",.1f")),p.duration(0);var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){p.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(a){p.data(a).hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){p.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.boxplot=e,b.xAxis=f,b.yAxis=g,b.tooltip=p,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},staggerLabels:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return l},set:function(a){l=a}},showYAxis:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return tooltips},set:function(a){tooltips=a}},tooltipContent:{get:function(){return p},set:function(a){p=a}},noData:{get:function(){return q},set:function(a){q=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!==a.top?a.top:h.top,h.right=void 0!==a.right?a.right:h.right,h.bottom=void 0!==a.bottom?a.bottom:h.bottom,h.left=void 0!==a.left?a.left:h.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}},rightAlignYAxis:{get:function(){return n},set:function(a){n=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.bullet=function(){"use strict";function b(d){return d.each(function(b,d){var p=m-c.left-c.right,s=n-c.top-c.bottom;o=d3.select(this),a.utils.initSVG(o);{var t=f.call(this,b,d).slice().sort(d3.descending),u=g.call(this,b,d).slice().sort(d3.descending),v=h.call(this,b,d).slice().sort(d3.descending),w=i.call(this,b,d).slice(),x=j.call(this,b,d).slice(),y=k.call(this,b,d).slice(),z=d3.scale.linear().domain(d3.extent(d3.merge([l,t]))).range(e?[p,0]:[0,p]);this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range())}this.__chart__=z;var A=d3.min(t),B=d3.max(t),C=t[1],D=o.selectAll("g.nv-wrap.nv-bullet").data([b]),E=D.enter().append("g").attr("class","nvd3 nv-wrap nv-bullet"),F=E.append("g"),G=D.select("g");F.append("rect").attr("class","nv-range nv-rangeMax"),F.append("rect").attr("class","nv-range nv-rangeAvg"),F.append("rect").attr("class","nv-range nv-rangeMin"),F.append("rect").attr("class","nv-measure"),D.attr("transform","translate("+c.left+","+c.top+")");var H=function(a){return Math.abs(z(a)-z(0))},I=function(a){return z(0>a?a:0)};G.select("rect.nv-rangeMax").attr("height",s).attr("width",H(B>0?B:A)).attr("x",I(B>0?B:A)).datum(B>0?B:A),G.select("rect.nv-rangeAvg").attr("height",s).attr("width",H(C)).attr("x",I(C)).datum(C),G.select("rect.nv-rangeMin").attr("height",s).attr("width",H(B)).attr("x",I(B)).attr("width",H(B>0?A:B)).attr("x",I(B>0?A:B)).datum(B>0?A:B),G.select("rect.nv-measure").style("fill",q).attr("height",s/3).attr("y",s/3).attr("width",0>v?z(0)-z(v[0]):z(v[0])-z(0)).attr("x",I(v)).on("mouseover",function(){r.elementMouseover({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})}).on("mouseout",function(){r.elementMouseout({value:v[0],label:y[0]||"Current",color:d3.select(this).style("fill")})});var J=s/6,K=u.map(function(a,b){return{value:a,label:x[b]}});F.selectAll("path.nv-markerTriangle").data(K).enter().append("path").attr("class","nv-markerTriangle").attr("transform",function(a){return"translate("+z(a.value)+","+s/2+")"}).attr("d","M0,"+J+"L"+J+","+-J+" "+-J+","+-J+"Z").on("mouseover",function(a){r.elementMouseover({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill"),pos:[z(a.value),s/2]})}).on("mousemove",function(a){r.elementMousemove({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a){r.elementMouseout({value:a.value,label:a.label||"Previous",color:d3.select(this).style("fill")})}),D.selectAll(".nv-range").on("mouseover",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseover({value:a,label:c,color:d3.select(this).style("fill")})}).on("mousemove",function(){r.elementMousemove({value:v[0],label:y[0]||"Previous",color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){var c=w[b]||(b?1==b?"Mean":"Minimum":"Maximum");r.elementMouseout({value:a,label:c,color:d3.select(this).style("fill")})})}),b}var c={top:0,right:0,bottom:0,left:0},d="left",e=!1,f=function(a){return a.ranges},g=function(a){return a.markers?a.markers:[0]},h=function(a){return a.measures},i=function(a){return a.rangeLabels?a.rangeLabels:[]},j=function(a){return a.markerLabels?a.markerLabels:[]},k=function(a){return a.measureLabels?a.measureLabels:[]},l=[0],m=380,n=30,o=null,p=null,q=a.utils.getColor(["#1f77b4"]),r=d3.dispatch("elementMouseover","elementMouseout","elementMousemove");return b.dispatch=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return f},set:function(a){f=a}},markers:{get:function(){return g},set:function(a){g=a}},measures:{get:function(){return h},set:function(a){h=a}},forceX:{get:function(){return l},set:function(a){l=a}},width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},tickFormat:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},orient:{get:function(){return d},set:function(a){d=a,e="right"==d||"bottom"==d}},color:{get:function(){return q},set:function(b){q=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.bulletChart=function(){"use strict";function b(d){return d.each(function(e,o){var p=d3.select(this);a.utils.initSVG(p);var q=a.utils.availableWidth(k,p,g),r=l-g.top-g.bottom;if(b.update=function(){b(d)},b.container=this,!e||!h.call(this,e,o))return a.utils.noData(b,p),b;p.selectAll(".nv-noData").remove();var s=h.call(this,e,o).slice().sort(d3.descending),t=i.call(this,e,o).slice().sort(d3.descending),u=j.call(this,e,o).slice().sort(d3.descending),v=p.selectAll("g.nv-wrap.nv-bulletChart").data([e]),w=v.enter().append("g").attr("class","nvd3 nv-wrap nv-bulletChart"),x=w.append("g"),y=v.select("g");x.append("g").attr("class","nv-bulletWrap"),x.append("g").attr("class","nv-titles"),v.attr("transform","translate("+g.left+","+g.top+")");var z=d3.scale.linear().domain([0,Math.max(s[0],t[0],u[0])]).range(f?[q,0]:[0,q]),A=this.__chart__||d3.scale.linear().domain([0,1/0]).range(z.range());this.__chart__=z;var B=x.select(".nv-titles").append("g").attr("text-anchor","end").attr("transform","translate(-6,"+(l-g.top-g.bottom)/2+")");B.append("text").attr("class","nv-title").text(function(a){return a.title}),B.append("text").attr("class","nv-subtitle").attr("dy","1em").text(function(a){return a.subtitle}),c.width(q).height(r);var C=y.select(".nv-bulletWrap");d3.transition(C).call(c);var D=m||z.tickFormat(q/100),E=y.selectAll("g.nv-tick").data(z.ticks(n?n:q/50),function(a){return this.textContent||D(a)}),F=E.enter().append("g").attr("class","nv-tick").attr("transform",function(a){return"translate("+A(a)+",0)"}).style("opacity",1e-6);F.append("line").attr("y1",r).attr("y2",7*r/6),F.append("text").attr("text-anchor","middle").attr("dy","1em").attr("y",7*r/6).text(D);var G=d3.transition(E).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1);G.select("line").attr("y1",r).attr("y2",7*r/6),G.select("text").attr("y",7*r/6),d3.transition(E.exit()).attr("transform",function(a){return"translate("+z(a)+",0)"}).style("opacity",1e-6).remove()}),d3.timer.flush(),b}var c=a.models.bullet(),d=a.models.tooltip(),e="left",f=!1,g={top:5,right:40,bottom:20,left:120},h=function(a){return a.ranges},i=function(a){return a.markers?a.markers:[0]},j=function(a){return a.measures},k=null,l=55,m=null,n=null,o=null,p=d3.dispatch("tooltipShow","tooltipHide");return d.duration(0).headerEnabled(!1),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.label,value:a.value,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.bullet=c,b.dispatch=p,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{ranges:{get:function(){return h},set:function(a){h=a}},markers:{get:function(){return i},set:function(a){i=a}},measures:{get:function(){return j},set:function(a){j=a}},width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},tickFormat:{get:function(){return m},set:function(a){m=a}},ticks:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return o},set:function(a){o=a}},tooltips:{get:function(){return d.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),d.enabled(!!b)}},tooltipContent:{get:function(){return d.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),d.contentGenerator(b)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},orient:{get:function(){return e},set:function(a){e=a,f="right"==e||"bottom"==e}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.candlestickBar=function(){"use strict";function b(x){return x.each(function(b){c=d3.select(this);var x=a.utils.availableWidth(i,c,h),y=a.utils.availableHeight(j,c,h);a.utils.initSVG(c);var A=x/b[0].values.length*.45;l.domain(d||d3.extent(b[0].values.map(n).concat(t))),l.range(v?f||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:f||[5+A/2,x-A/2-5]),m.domain(e||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(g||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var B=d3.select(this).selectAll("g.nv-wrap.nv-candlestickBar").data([b[0].values]),C=B.enter().append("g").attr("class","nvd3 nv-wrap nv-candlestickBar"),D=C.append("defs"),E=C.append("g"),F=B.select("g");E.append("g").attr("class","nv-ticks"),B.attr("transform","translate("+h.left+","+h.top+")"),c.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:k})}),D.append("clipPath").attr("id","nv-chart-clip-path-"+k).append("rect"),B.select("#nv-chart-clip-path-"+k+" rect").attr("width",x).attr("height",y),F.attr("clip-path",w?"url(#nv-chart-clip-path-"+k+")":"");var G=B.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});G.exit().remove();{var H=G.enter().append("g").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b});H.append("line").attr("class","nv-candlestick-lines").attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),H.append("rect").attr("class","nv-candlestick-rects nv-bars").attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}c.selectAll(".nv-candlestick-lines").transition().attr("transform",function(a,b){return"translate("+l(n(a,b))+",0)"}).attr("x1",0).attr("y1",function(a,b){return m(r(a,b))}).attr("x2",0).attr("y2",function(a,b){return m(s(a,b))}),c.selectAll(".nv-candlestick-rects").transition().attr("transform",function(a,b){return"translate("+(l(n(a,b))-A/2)+","+(m(o(a,b))-(p(a,b)>q(a,b)?m(q(a,b))-m(p(a,b)):0))+")"}).attr("x",0).attr("y",0).attr("width",A).attr("height",function(a,b){var c=p(a,b),d=q(a,b);return c>d?m(d)-m(c):m(c)-m(d)})}),b}var c,d,e,f,g,h={top:0,right:0,bottom:0,left:0},i=null,j=null,k=Math.floor(1e4*Math.random()),l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,d){b.clearHighlights(),c.select(".nv-candlestickBar .nv-tick-0-"+a).classed("hover",d)},b.clearHighlights=function(){c.select(".nv-candlestickBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return i},set:function(a){i=a}},height:{get:function(){return j},set:function(a){j=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return k},set:function(a){k=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return h},set:function(a){h.top=void 0!=a.top?a.top:h.top,h.right=void 0!=a.right?a.right:h.right,h.bottom=void 0!=a.bottom?a.bottom:h.bottom,h.left=void 0!=a.left?a.left:h.left}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.cumulativeLineChart=function(){"use strict";function b(l){return H.reset(),H.models(f),r&&H.models(g),s&&H.models(h),l.each(function(l){function A(){d3.select(b.container).style("cursor","ew-resize")}function E(){G.x=d3.event.x,G.i=Math.round(F.invert(G.x)),K()}function H(){d3.select(b.container).style("cursor","auto"),y.index=G.i,C.stateChange(y)}function K(){bb.data([G]);var a=b.duration();b.duration(0),b.update(),b.duration(a)}var L=d3.select(this);a.utils.initSVG(L),L.classed("nv-chart-"+x,!0);var M=this,N=a.utils.availableWidth(o,L,m),O=a.utils.availableHeight(p,L,m);if(b.update=function(){0===D?L.call(b):L.transition().duration(D).call(b)},b.container=this,y.setter(J(l),b.update).getter(I(l)).update(),y.disabled=l.map(function(a){return!!a.disabled}),!z){var P;z={};for(P in y)z[P]=y[P]instanceof Array?y[P].slice(0):y[P]}var Q=d3.behavior.drag().on("dragstart",A).on("drag",E).on("dragend",H);if(!(l&&l.length&&l.filter(function(a){return a.values.length}).length))return a.utils.noData(b,L),b;if(L.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale(),w)f.yDomain(null);else{var R=l.filter(function(a){return!a.disabled}).map(function(a){var b=d3.extent(a.values,f.y());return b[0]<-.95&&(b[0]=-.95),[(b[0]-b[1])/(1+b[1]),(b[1]-b[0])/(1+b[0])]}),S=[d3.min(R,function(a){return a[0]}),d3.max(R,function(a){return a[1]})];f.yDomain(S)}F.domain([0,l[0].values.length-1]).range([0,N]).clamp(!0);var l=c(G.i,l),T=v?"none":"all",U=L.selectAll("g.nv-wrap.nv-cumulativeLine").data([l]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-cumulativeLine").append("g"),W=U.select("g");if(V.append("g").attr("class","nv-interactive"),V.append("g").attr("class","nv-x nv-axis").style("pointer-events","none"),V.append("g").attr("class","nv-y nv-axis"),V.append("g").attr("class","nv-background"),V.append("g").attr("class","nv-linesWrap").style("pointer-events",T),V.append("g").attr("class","nv-avgLinesWrap").style("pointer-events","none"),V.append("g").attr("class","nv-legendWrap"),V.append("g").attr("class","nv-controlsWrap"),q&&(i.width(N),W.select(".nv-legendWrap").datum(l).call(i),m.top!=i.height()&&(m.top=i.height(),O=a.utils.availableHeight(p,L,m)),W.select(".nv-legendWrap").attr("transform","translate(0,"+-m.top+")")),u){var X=[{key:"Re-scale y-axis",disabled:!w}];j.width(140).color(["#444","#444","#444"]).rightAlign(!1).margin({top:5,right:0,bottom:5,left:20}),W.select(".nv-controlsWrap").datum(X).attr("transform","translate(0,"+-m.top+")").call(j)}U.attr("transform","translate("+m.left+","+m.top+")"),t&&W.select(".nv-y.nv-axis").attr("transform","translate("+N+",0)");var Y=l.filter(function(a){return a.tempDisabled});U.select(".tempDisabled").remove(),Y.length&&U.append("text").attr("class","tempDisabled").attr("x",N/2).attr("y","-.71em").style("text-anchor","end").text(Y.map(function(a){return a.key}).join(", ")+" values cannot be calculated for this time period."),v&&(k.width(N).height(O).margin({left:m.left,top:m.top}).svgContainer(L).xScale(d),U.select(".nv-interactive").call(k)),V.select(".nv-background").append("rect"),W.select(".nv-background rect").attr("width",N).attr("height",O),f.y(function(a){return a.display.y}).width(N).height(O).color(l.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!l[b].disabled&&!l[b].tempDisabled}));var Z=W.select(".nv-linesWrap").datum(l.filter(function(a){return!a.disabled&&!a.tempDisabled}));Z.call(f),l.forEach(function(a,b){a.seriesIndex=b});var $=l.filter(function(a){return!a.disabled&&!!B(a)}),_=W.select(".nv-avgLinesWrap").selectAll("line").data($,function(a){return a.key}),ab=function(a){var b=e(B(a));return 0>b?0:b>O?O:b};_.enter().append("line").style("stroke-width",2).style("stroke-dasharray","10,10").style("stroke",function(a){return f.color()(a,a.seriesIndex)}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.style("stroke-opacity",function(a){var b=e(B(a));return 0>b||b>O?0:1}).attr("x1",0).attr("x2",N).attr("y1",ab).attr("y2",ab),_.exit().remove();var bb=Z.selectAll(".nv-indexLine").data([G]);bb.enter().append("rect").attr("class","nv-indexLine").attr("width",3).attr("x",-2).attr("fill","red").attr("fill-opacity",.5).style("pointer-events","all").call(Q),bb.attr("transform",function(a){return"translate("+F(a.i)+",0)"}).attr("height",O),r&&(g.scale(d)._ticks(a.utils.calcTicksX(N/70,l)).tickSize(-O,0),W.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),W.select(".nv-x.nv-axis").call(g)),s&&(h.scale(e)._ticks(a.utils.calcTicksY(O/36,l)).tickSize(-N,0),W.select(".nv-y.nv-axis").call(h)),W.select(".nv-background rect").on("click",function(){G.x=d3.mouse(this)[0],G.i=Math.round(F.invert(G.x)),y.index=G.i,C.stateChange(y),K()}),f.dispatch.on("elementClick",function(a){G.i=a.pointIndex,G.x=F(G.i),y.index=G.i,C.stateChange(y),K()}),j.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,w=!a.disabled,y.rescaleY=w,C.stateChange(y),b.update()}),i.dispatch.on("stateChange",function(a){for(var c in a)y[c]=a[c];C.stateChange(y),b.update()}),k.dispatch.on("elementMousemove",function(c){f.clearHighlights();var d,e,i,j=[];if(l.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g,h){e=a.interactiveBisect(g.values,c.pointXValue,b.x()),f.highlightPoint(h,e,!0);var k=g.values[e];"undefined"!=typeof k&&("undefined"==typeof d&&(d=k),"undefined"==typeof i&&(i=b.xScale()(b.x()(k,e))),j.push({key:g.key,value:b.y()(k,e),color:n(g,g.seriesIndex)}))}),j.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(j.map(function(a){return a.value}),o,q);null!==r&&(j[r].highlight=!0)}var s=g.tickFormat()(b.x()(d,e),e);k.tooltip.position({left:i+m.left,top:c.mouseY+m.top}).chartContainer(M.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:s,series:j})(),k.renderGuideLine(i)}),k.dispatch.on("elementMouseout",function(){f.clearHighlights()}),C.on("changeState",function(a){"undefined"!=typeof a.disabled&&(l.forEach(function(b,c){b.disabled=a.disabled[c]}),y.disabled=a.disabled),"undefined"!=typeof a.index&&(G.i=a.index,G.x=F(G.i),y.index=a.index,bb.data([G])),"undefined"!=typeof a.rescaleY&&(w=a.rescaleY),b.update()})}),H.renderEnd("cumulativeLineChart immediate"),b}function c(a,b){return K||(K=f.y()),b.map(function(b){if(!b.values)return b;var c=b.values[a];if(null==c)return b;var d=K(c,a);return-.95>d&&!E?(b.tempDisabled=!0,b):(b.tempDisabled=!1,b.values=b.values.map(function(a,b){return a.display={y:(K(a,b)-d)/(1+d)},a}),b)})}var d,e,f=a.models.line(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.models.legend(),k=a.interactiveGuideline(),l=a.models.tooltip(),m={top:30,right:30,bottom:50,left:60},n=a.utils.defaultColor(),o=null,p=null,q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=!0,x=f.id(),y=a.utils.state(),z=null,A=null,B=function(a){return a.average},C=d3.dispatch("stateChange","changeState","renderEnd"),D=250,E=!1;y.index=0,y.rescaleY=w,g.orient("bottom").tickPadding(7),h.orient(t?"right":"left"),l.valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)}),j.updateState(!1);var F=d3.scale.linear(),G={i:0,x:0},H=a.utils.renderWatch(C,D),I=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),index:G.i,rescaleY:w}}},J=function(a){return function(b){void 0!==b.index&&(G.i=b.index),void 0!==b.rescaleY&&(w=b.rescaleY),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};f.dispatch.on("elementMouseover.tooltip",function(a){var c={x:b.x()(a.point),y:b.y()(a.point),color:a.point.color};a.point=c,l.data(a).position(a.pos).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){l.hidden(!0)});var K=null;return b.dispatch=C,b.lines=f,b.legend=i,b.controls=j,b.xAxis=g,b.yAxis=h,b.interactiveLayer=k,b.state=y,b.tooltip=l,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return o},set:function(a){o=a}},height:{get:function(){return p},set:function(a){p=a}},rescaleY:{get:function(){return w},set:function(a){w=a}},showControls:{get:function(){return u},set:function(a){u=a}},showLegend:{get:function(){return q},set:function(a){q=a}},average:{get:function(){return B},set:function(a){B=a}},defaultState:{get:function(){return z},set:function(a){z=a}},noData:{get:function(){return A},set:function(a){A=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},noErrorCheck:{get:function(){return E},set:function(a){E=a}},tooltips:{get:function(){return l.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),l.enabled(!!b)}},tooltipContent:{get:function(){return l.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),l.contentGenerator(b)}},margin:{get:function(){return m},set:function(a){m.top=void 0!==a.top?a.top:m.top,m.right=void 0!==a.right?a.right:m.right,m.bottom=void 0!==a.bottom?a.bottom:m.bottom,m.left=void 0!==a.left?a.left:m.left}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),i.color(n)}},useInteractiveGuideline:{get:function(){return v},set:function(a){v=a,a===!0&&(b.interactive(!1),b.useVoronoi(!1))}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,h.orient(a?"right":"left")}},duration:{get:function(){return D},set:function(a){D=a,f.duration(D),g.duration(D),h.duration(D),H.reset(D)}}}),a.utils.inheritOptions(b,f),a.utils.initOptions(b),b},a.models.discreteBar=function(){"use strict";function b(m){return y.reset(),m.each(function(b){var m=k-j.left-j.right,x=l-j.top-j.bottom;c=d3.select(this),a.utils.initSVG(c),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var z=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),y0:a.y0}})});n.domain(d||d3.merge(z).map(function(a){return a.x})).rangeBands(f||[0,m],.1),o.domain(e||d3.extent(d3.merge(z).map(function(a){return a.y}).concat(r))),o.range(t?g||[x-(o.domain()[0]<0?12:0),o.domain()[1]>0?12:0]:g||[x,0]),h=h||n,i=i||o.copy().range([o(0),o(0)]);{var A=c.selectAll("g.nv-wrap.nv-discretebar").data([b]),B=A.enter().append("g").attr("class","nvd3 nv-wrap nv-discretebar"),C=B.append("g");A.select("g")}C.append("g").attr("class","nv-groups"),A.attr("transform","translate("+j.left+","+j.top+")");var D=A.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});D.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),D.exit().watchTransition(y,"discreteBar: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),D.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),D.watchTransition(y,"discreteBar: groups").style("stroke-opacity",1).style("fill-opacity",.75);var E=D.selectAll("g.nv-bar").data(function(a){return a.values});E.exit().remove();var F=E.enter().append("g").attr("transform",function(a,b){return"translate("+(n(p(a,b))+.05*n.rangeBand())+", "+o(0)+")"}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),v.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),v.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){v.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){v.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()});F.append("rect").attr("height",0).attr("width",.9*n.rangeBand()/b.length),t?(F.append("text").attr("text-anchor","middle"),E.select("text").text(function(a,b){return u(q(a,b))}).watchTransition(y,"discreteBar: bars text").attr("x",.9*n.rangeBand()/2).attr("y",function(a,b){return q(a,b)<0?o(q(a,b))-o(0)+12:-4})):E.selectAll("text").remove(),E.attr("class",function(a,b){return q(a,b)<0?"nv-bar negative":"nv-bar positive"}).style("fill",function(a,b){return a.color||s(a,b)}).style("stroke",function(a,b){return a.color||s(a,b)}).select("rect").attr("class",w).watchTransition(y,"discreteBar: bars rect").attr("width",.9*n.rangeBand()/b.length),E.watchTransition(y,"discreteBar: bars").attr("transform",function(a,b){var c=n(p(a,b))+.05*n.rangeBand(),d=q(a,b)<0?o(0):o(0)-o(q(a,b))<1?o(0)-1:o(q(a,b));return"translate("+c+", "+d+")"}).select("rect").attr("height",function(a,b){return Math.max(Math.abs(o(q(a,b))-o(e&&e[0]||0))||1)}),h=n.copy(),i=o.copy()}),y.renderEnd("discreteBar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=d3.scale.ordinal(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=[0],s=a.utils.defaultColor(),t=!1,u=d3.format(",.2f"),v=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),w="discreteBar",x=250,y=a.utils.renderWatch(v,x);return b.dispatch=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},forceY:{get:function(){return r},set:function(a){r=a}},showValues:{get:function(){return t},set:function(a){t=a}},x:{get:function(){return p},set:function(a){p=a}},y:{get:function(){return q},set:function(a){q=a}},xScale:{get:function(){return n},set:function(a){n=a}},yScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},valueFormat:{get:function(){return u},set:function(a){u=a}},id:{get:function(){return m},set:function(a){m=a}},rectClass:{get:function(){return w},set:function(a){w=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b)}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x)}}}),a.utils.initOptions(b),b},a.models.discreteBarChart=function(){"use strict";function b(h){return t.reset(),t.models(e),m&&t.models(f),n&&t.models(g),h.each(function(h){var l=d3.select(this);a.utils.initSVG(l);var q=a.utils.availableWidth(j,l,i),t=a.utils.availableHeight(k,l,i);if(b.update=function(){r.beforeUpdate(),l.transition().duration(s).call(b)},b.container=this,!(h&&h.length&&h.filter(function(a){return a.values.length}).length))return a.utils.noData(b,l),b;l.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale().clamp(!0);var u=l.selectAll("g.nv-wrap.nv-discreteBarWithAxes").data([h]),v=u.enter().append("g").attr("class","nvd3 nv-wrap nv-discreteBarWithAxes").append("g"),w=v.append("defs"),x=u.select("g");v.append("g").attr("class","nv-x nv-axis"),v.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),v.append("g").attr("class","nv-barsWrap"),x.attr("transform","translate("+i.left+","+i.top+")"),o&&x.select(".nv-y.nv-axis").attr("transform","translate("+q+",0)"),e.width(q).height(t);var y=x.select(".nv-barsWrap").datum(h.filter(function(a){return!a.disabled}));if(y.transition().call(e),w.append("clipPath").attr("id","nv-x-label-clip-"+e.id()).append("rect"),x.select("#nv-x-label-clip-"+e.id()+" rect").attr("width",c.rangeBand()*(p?2:1)).attr("height",16).attr("x",-c.rangeBand()/(p?1:2)),m){f.scale(c)._ticks(a.utils.calcTicksX(q/100,h)).tickSize(-t,0),x.select(".nv-x.nv-axis").attr("transform","translate(0,"+(d.range()[0]+(e.showValues()&&d.domain()[0]<0?16:0))+")"),x.select(".nv-x.nv-axis").call(f); -var z=x.select(".nv-x.nv-axis").selectAll("g");p&&z.selectAll("text").attr("transform",function(a,b,c){return"translate(0,"+(c%2==0?"5":"17")+")"})}n&&(g.scale(d)._ticks(a.utils.calcTicksY(t/36,h)).tickSize(-q,0),x.select(".nv-y.nv-axis").call(g)),x.select(".nv-zeroLine line").attr("x1",0).attr("x2",q).attr("y1",d(0)).attr("y2",d(0))}),t.renderEnd("discreteBar chart immediate"),b}var c,d,e=a.models.discreteBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.tooltip(),i={top:15,right:10,bottom:50,left:60},j=null,k=null,l=a.utils.getColor(),m=!0,n=!0,o=!1,p=!1,q=null,r=d3.dispatch("beforeUpdate","renderEnd"),s=250;f.orient("bottom").showMaxMin(!1).tickFormat(function(a){return a}),g.orient(o?"right":"left").tickFormat(d3.format(",.1f")),h.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).keyFormatter(function(a,b){return f.tickFormat()(a,b)});var t=a.utils.renderWatch(r,s);return e.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},h.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){h.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){h.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=r,b.discretebar=e,b.xAxis=f,b.yAxis=g,b.tooltip=h,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return j},set:function(a){j=a}},height:{get:function(){return k},set:function(a){k=a}},staggerLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return m},set:function(a){m=a}},showYAxis:{get:function(){return n},set:function(a){n=a}},noData:{get:function(){return q},set:function(a){q=a}},tooltips:{get:function(){return h.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),h.enabled(!!b)}},tooltipContent:{get:function(){return h.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),h.contentGenerator(b)}},margin:{get:function(){return i},set:function(a){i.top=void 0!==a.top?a.top:i.top,i.right=void 0!==a.right?a.right:i.right,i.bottom=void 0!==a.bottom?a.bottom:i.bottom,i.left=void 0!==a.left?a.left:i.left}},duration:{get:function(){return s},set:function(a){s=a,t.reset(s),e.duration(s),f.duration(s),g.duration(s)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),e.color(l)}},rightAlignYAxis:{get:function(){return o},set:function(a){o=a,g.orient(a?"right":"left")}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.distribution=function(){"use strict";function b(k){return m.reset(),k.each(function(b){var k=(e-("x"===g?d.left+d.right:d.top+d.bottom),"x"==g?"y":"x"),l=d3.select(this);a.utils.initSVG(l),c=c||j;var n=l.selectAll("g.nv-distribution").data([b]),o=n.enter().append("g").attr("class","nvd3 nv-distribution"),p=(o.append("g"),n.select("g"));n.attr("transform","translate("+d.left+","+d.top+")");var q=p.selectAll("g.nv-dist").data(function(a){return a},function(a){return a.key});q.enter().append("g"),q.attr("class",function(a,b){return"nv-dist nv-series-"+b}).style("stroke",function(a,b){return i(a,b)});var r=q.selectAll("line.nv-dist"+g).data(function(a){return a.values});r.enter().append("line").attr(g+"1",function(a,b){return c(h(a,b))}).attr(g+"2",function(a,b){return c(h(a,b))}),m.transition(q.exit().selectAll("line.nv-dist"+g),"dist exit").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}).style("stroke-opacity",0).remove(),r.attr("class",function(a,b){return"nv-dist"+g+" nv-dist"+g+"-"+b}).attr(k+"1",0).attr(k+"2",f),m.transition(r,"dist").attr(g+"1",function(a,b){return j(h(a,b))}).attr(g+"2",function(a,b){return j(h(a,b))}),c=j.copy()}),m.renderEnd("distribution immediate"),b}var c,d={top:0,right:0,bottom:0,left:0},e=400,f=8,g="x",h=function(a){return a[g]},i=a.utils.defaultColor(),j=d3.scale.linear(),k=250,l=d3.dispatch("renderEnd"),m=a.utils.renderWatch(l,k);return b.options=a.utils.optionsFunc.bind(b),b.dispatch=l,b.margin=function(a){return arguments.length?(d.top="undefined"!=typeof a.top?a.top:d.top,d.right="undefined"!=typeof a.right?a.right:d.right,d.bottom="undefined"!=typeof a.bottom?a.bottom:d.bottom,d.left="undefined"!=typeof a.left?a.left:d.left,b):d},b.width=function(a){return arguments.length?(e=a,b):e},b.axis=function(a){return arguments.length?(g=a,b):g},b.size=function(a){return arguments.length?(f=a,b):f},b.getData=function(a){return arguments.length?(h=d3.functor(a),b):h},b.scale=function(a){return arguments.length?(j=a,b):j},b.color=function(c){return arguments.length?(i=a.utils.getColor(c),b):i},b.duration=function(a){return arguments.length?(k=a,m.reset(k),b):k},b},a.models.furiousLegend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?g(a,b):"#fff":m?void 0:a.disabled?g(a,b):"#fff"}function r(a,b){return m&&"furious"==o?a.disengaged?"#fff":g(a,b):a.disabled?"#fff":g(a,b)}return p.each(function(b){var p=d-c.left-c.right,s=d3.select(this);a.utils.initSVG(s);var t=s.selectAll("g.nv-legend").data([b]),u=(t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),t.select("g"));t.attr("transform","translate("+c.left+","+c.top+")");var v,w=u.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),x=w.enter().append("g").attr("class","nv-series");if("classic"==o)x.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),v=w.select("circle");else if("furious"==o){x.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),v=w.select("rect"),x.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var y=w.select(".nv-check-box");y.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}x.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var z=w.select("text.nv-legend-text");w.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=w.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=w.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),w.classed("nv-disabled",function(a){return a.userDisabled}),w.exit().remove(),z.attr("fill",q).text(f);var A;switch(o){case"furious":A=23;break;case"classic":A=20}if(h){var B=[];w.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}B.push(b+i)});for(var C=0,D=0,E=[];p>D&&Cp&&C>1;){E=[],C--;for(var F=0;F(E[F%C]||0)&&(E[F%C]=B[F]);D=E.reduce(function(a,b){return a+b})}for(var G=[],H=0,I=0;C>H;H++)G[H]=I,I+=E[H];w.attr("transform",function(a,b){return"translate("+G[b%C]+","+(5+Math.floor(b/C)*A)+")"}),j?u.attr("transform","translate("+(d-c.right-D)+","+c.top+")"):u.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(B.length/C)*A}else{var J,K=5,L=5,M=0;w.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return J=L,dM&&(M=L),"translate("+J+","+K+")"}),u.attr("transform","translate("+(d-c.right-M)+","+c.top+")"),e=c.top+c.bottom+K+15}"furious"==o&&v.attr("width",function(a,b){return z[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),v.style("fill",r).style("stroke",function(a,b){return a.color||g(a,b)})}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=28,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBar=function(){"use strict";function b(x){return x.each(function(b){w.reset(),k=d3.select(this);var x=a.utils.availableWidth(h,k,g),y=a.utils.availableHeight(i,k,g);a.utils.initSVG(k),l.domain(c||d3.extent(b[0].values.map(n).concat(p))),l.range(r?e||[.5*x/b[0].values.length,x*(b[0].values.length-.5)/b[0].values.length]:e||[0,x]),m.domain(d||d3.extent(b[0].values.map(o).concat(q))).range(f||[y,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var z=k.selectAll("g.nv-wrap.nv-historicalBar-"+j).data([b[0].values]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBar-"+j),B=A.append("defs"),C=A.append("g"),D=z.select("g");C.append("g").attr("class","nv-bars"),z.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){u.chartClick({data:a,index:b,pos:d3.event,id:j})}),B.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),z.select("#nv-chart-clip-path-"+j+" rect").attr("width",x).attr("height",y),D.attr("clip-path",s?"url(#nv-chart-clip-path-"+j+")":"");var E=z.select(".nv-bars").selectAll(".nv-bar").data(function(a){return a},function(a,b){return n(a,b)});E.exit().remove(),E.enter().append("rect").attr("x",0).attr("y",function(b,c){return a.utils.NaNtoZero(m(Math.max(0,o(b,c))))}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.abs(m(o(b,c))-m(0)))}).attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).on("mouseover",function(a,b){v&&(d3.select(this).classed("hover",!0),u.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mouseout",function(a,b){v&&(d3.select(this).classed("hover",!1),u.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")}))}).on("mousemove",function(a,b){v&&u.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){v&&(u.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}).on("dblclick",function(a,b){v&&(u.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation())}),E.attr("fill",function(a,b){return t(a,b)}).attr("class",function(a,b,c){return(o(a,b)<0?"nv-bar negative":"nv-bar positive")+" nv-bar-"+c+"-"+b}).watchTransition(w,"bars").attr("transform",function(a,c){return"translate("+(l(n(a,c))-x/b[0].values.length*.45)+",0)"}).attr("width",x/b[0].values.length*.9),E.watchTransition(w,"bars").attr("y",function(b,c){var d=o(b,c)<0?m(0):m(0)-m(o(b,c))<1?m(0)-1:m(o(b,c));return a.utils.NaNtoZero(d)}).attr("height",function(b,c){return a.utils.NaNtoZero(Math.max(Math.abs(m(o(b,c))-m(0)),1))})}),w.renderEnd("historicalBar immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=[],q=[0],r=!1,s=!0,t=a.utils.defaultColor(),u=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),v=!0,w=a.utils.renderWatch(u,0);return b.highlightPoint=function(a,b){k.select(".nv-bars .nv-bar-0-"+a).classed("hover",b)},b.clearHighlights=function(){k.select(".nv-bars .nv-bar.hover").classed("hover",!1)},b.dispatch=u,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},forceX:{get:function(){return p},set:function(a){p=a}},forceY:{get:function(){return q},set:function(a){q=a}},padData:{get:function(){return r},set:function(a){r=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},clipEdge:{get:function(){return s},set:function(a){s=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return v},set:function(a){v=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return t},set:function(b){t=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.historicalBarChart=function(b){"use strict";function c(b){return b.each(function(k){z.reset(),z.models(f),q&&z.models(g),r&&z.models(h);var w=d3.select(this),A=this;a.utils.initSVG(w);var B=a.utils.availableWidth(n,w,l),C=a.utils.availableHeight(o,w,l);if(c.update=function(){w.transition().duration(y).call(c)},c.container=this,u.disabled=k.map(function(a){return!!a.disabled}),!v){var D;v={};for(D in u)v[D]=u[D]instanceof Array?u[D].slice(0):u[D]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(c,w),c;w.selectAll(".nv-noData").remove(),d=f.xScale(),e=f.yScale();var E=w.selectAll("g.nv-wrap.nv-historicalBarChart").data([k]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-historicalBarChart").append("g"),G=E.select("g");F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-barsWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),p&&(i.width(B),G.select(".nv-legendWrap").datum(k).call(i),l.top!=i.height()&&(l.top=i.height(),C=a.utils.availableHeight(o,w,l)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-l.top+")")),E.attr("transform","translate("+l.left+","+l.top+")"),s&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),t&&(j.width(B).height(C).margin({left:l.left,top:l.top}).svgContainer(w).xScale(d),E.select(".nv-interactive").call(j)),f.width(B).height(C).color(k.map(function(a,b){return a.color||m(a,b)}).filter(function(a,b){return!k[b].disabled}));var H=G.select(".nv-barsWrap").datum(k.filter(function(a){return!a.disabled}));H.transition().call(f),q&&(g.scale(d)._ticks(a.utils.calcTicksX(B/100,k)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+e.range()[0]+")"),G.select(".nv-x.nv-axis").transition().call(g)),r&&(h.scale(e)._ticks(a.utils.calcTicksY(C/36,k)).tickSize(-B,0),G.select(".nv-y.nv-axis").transition().call(h)),j.dispatch.on("elementMousemove",function(b){f.clearHighlights();var d,e,i,n=[];k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(g){e=a.interactiveBisect(g.values,b.pointXValue,c.x()),f.highlightPoint(e,!0);var h=g.values[e];void 0!==h&&(void 0===d&&(d=h),void 0===i&&(i=c.xScale()(c.x()(h,e))),n.push({key:g.key,value:c.y()(h,e),color:m(g,g.seriesIndex),data:g.values[e]}))});var o=g.tickFormat()(c.x()(d,e));j.tooltip.position({left:i+l.left,top:b.mouseY+l.top}).chartContainer(A.parentNode).valueFormatter(function(a){return h.tickFormat()(a)}).data({value:o,index:e,series:n})(),j.renderGuideLine(i)}),j.dispatch.on("elementMouseout",function(){x.tooltipHide(),f.clearHighlights()}),i.dispatch.on("legendClick",function(a){a.disabled=!a.disabled,k.filter(function(a){return!a.disabled}).length||k.map(function(a){return a.disabled=!1,E.selectAll(".nv-series").classed("disabled",!1),a}),u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),b.transition().call(c)}),i.dispatch.on("legendDblclick",function(a){k.forEach(function(a){a.disabled=!0}),a.disabled=!1,u.disabled=k.map(function(a){return!!a.disabled}),x.stateChange(u),c.update()}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),c.update()})}),z.renderEnd("historicalBarChart immediate"),c}var d,e,f=b||a.models.historicalBar(),g=a.models.axis(),h=a.models.axis(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:90,bottom:50,left:90},m=a.utils.defaultColor(),n=null,o=null,p=!1,q=!0,r=!0,s=!1,t=!1,u={},v=null,w=null,x=d3.dispatch("tooltipHide","stateChange","changeState","renderEnd"),y=250;g.orient("bottom").tickPadding(7),h.orient(s?"right":"left"),k.duration(0).headerEnabled(!1).valueFormatter(function(a,b){return h.tickFormat()(a,b)}).headerFormatter(function(a,b){return g.tickFormat()(a,b)});var z=a.utils.renderWatch(x,0);return f.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:c.x()(a.data),value:c.y()(a.data),color:a.color},k.data(a).hidden(!1)}),f.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),f.dispatch.on("elementMousemove.tooltip",function(){k.position({top:d3.event.pageY,left:d3.event.pageX})()}),c.dispatch=x,c.bars=f,c.legend=i,c.xAxis=g,c.yAxis=h,c.interactiveLayer=j,c.tooltip=k,c.options=a.utils.optionsFunc.bind(c),c._options=Object.create({},{width:{get:function(){return n},set:function(a){n=a}},height:{get:function(){return o},set:function(a){o=a}},showLegend:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return q},set:function(a){q=a}},showYAxis:{get:function(){return r},set:function(a){r=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b),i.color(m),f.color(m)}},duration:{get:function(){return y},set:function(a){y=a,z.reset(y),h.duration(y),g.duration(y)}},rightAlignYAxis:{get:function(){return s},set:function(a){s=a,h.orient(a?"right":"left")}},useInteractiveGuideline:{get:function(){return t},set:function(a){t=a,a===!0&&c.interactive(!1)}}}),a.utils.inheritOptions(c,f),a.utils.initOptions(c),c},a.models.ohlcBarChart=function(){var b=a.models.historicalBarChart(a.models.ohlcBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
      open:"+b.yAxis.tickFormat()(c.open)+"
      close:"+b.yAxis.tickFormat()(c.close)+"
      high"+b.yAxis.tickFormat()(c.high)+"
      low:"+b.yAxis.tickFormat()(c.low)+"
      "}),b},a.models.candlestickBarChart=function(){var b=a.models.historicalBarChart(a.models.candlestickBar());return b.useInteractiveGuideline(!0),b.interactiveLayer.tooltip.contentGenerator(function(a){var c=a.series[0].data,d=c.open'+a.value+"
      open:"+b.yAxis.tickFormat()(c.open)+"
      close:"+b.yAxis.tickFormat()(c.close)+"
      high"+b.yAxis.tickFormat()(c.high)+"
      low:"+b.yAxis.tickFormat()(c.low)+"
      "}),b},a.models.legend=function(){"use strict";function b(p){function q(a,b){return"furious"!=o?"#000":m?a.disengaged?"#000":"#fff":m?void 0:(a.color||(a.color=g(a,b)),a.disabled?a.color:"#fff")}function r(a,b){return m&&"furious"==o&&a.disengaged?"#eee":a.color||g(a,b)}function s(a){return m&&"furious"==o?1:a.disabled?0:1}return p.each(function(b){var g=d-c.left-c.right,p=d3.select(this);a.utils.initSVG(p);var t=p.selectAll("g.nv-legend").data([b]),u=t.enter().append("g").attr("class","nvd3 nv-legend").append("g"),v=t.select("g");t.attr("transform","translate("+c.left+","+c.top+")");var w,x,y=v.selectAll(".nv-series").data(function(a){return"furious"!=o?a:a.filter(function(a){return m?!0:!a.disengaged})}),z=y.enter().append("g").attr("class","nv-series");switch(o){case"furious":x=23;break;case"classic":x=20}if("classic"==o)z.append("circle").style("stroke-width",2).attr("class","nv-legend-symbol").attr("r",5),w=y.select("circle");else if("furious"==o){z.append("rect").style("stroke-width",2).attr("class","nv-legend-symbol").attr("rx",3).attr("ry",3),w=y.select(".nv-legend-symbol"),z.append("g").attr("class","nv-check-box").property("innerHTML",'').attr("transform","translate(-10,-8)scale(0.5)");var A=y.select(".nv-check-box");A.each(function(a,b){d3.select(this).selectAll("path").attr("stroke",q(a,b))})}z.append("text").attr("text-anchor","start").attr("class","nv-legend-text").attr("dy",".32em").attr("dx","8");var B=y.select("text.nv-legend-text");y.on("mouseover",function(a,b){n.legendMouseover(a,b)}).on("mouseout",function(a,b){n.legendMouseout(a,b)}).on("click",function(a,b){n.legendClick(a,b);var c=y.data();if(k){if("classic"==o)l?(c.forEach(function(a){a.disabled=!0}),a.disabled=!1):(a.disabled=!a.disabled,c.every(function(a){return a.disabled})&&c.forEach(function(a){a.disabled=!1}));else if("furious"==o)if(m)a.disengaged=!a.disengaged,a.userDisabled=void 0==a.userDisabled?!!a.disabled:a.userDisabled,a.disabled=a.disengaged||a.userDisabled;else if(!m){a.disabled=!a.disabled,a.userDisabled=a.disabled;var d=c.filter(function(a){return!a.disengaged});d.every(function(a){return a.userDisabled})&&c.forEach(function(a){a.disabled=a.userDisabled=!1})}n.stateChange({disabled:c.map(function(a){return!!a.disabled}),disengaged:c.map(function(a){return!!a.disengaged})})}}).on("dblclick",function(a,b){if(("furious"!=o||!m)&&(n.legendDblclick(a,b),k)){var c=y.data();c.forEach(function(a){a.disabled=!0,"furious"==o&&(a.userDisabled=a.disabled)}),a.disabled=!1,"furious"==o&&(a.userDisabled=a.disabled),n.stateChange({disabled:c.map(function(a){return!!a.disabled})})}}),y.classed("nv-disabled",function(a){return a.userDisabled}),y.exit().remove(),B.attr("fill",q).text(f);var C=0;if(h){var D=[];y.each(function(){var b,c=d3.select(this).select("text");try{if(b=c.node().getComputedTextLength(),0>=b)throw Error()}catch(d){b=a.utils.calcApproxTextWidth(c)}D.push(b+i)});var E=0,F=[];for(C=0;g>C&&Eg&&E>1;){F=[],E--;for(var G=0;G(F[G%E]||0)&&(F[G%E]=D[G]);C=F.reduce(function(a,b){return a+b})}for(var H=[],I=0,J=0;E>I;I++)H[I]=J,J+=F[I];y.attr("transform",function(a,b){return"translate("+H[b%E]+","+(5+Math.floor(b/E)*x)+")"}),j?v.attr("transform","translate("+(d-c.right-C)+","+c.top+")"):v.attr("transform","translate(0,"+c.top+")"),e=c.top+c.bottom+Math.ceil(D.length/E)*x}else{var K,L=5,M=5,N=0;y.attr("transform",function(){var a=d3.select(this).select("text").node().getComputedTextLength()+i;return K=M,dN&&(N=M),K+N>C&&(C=K+N),"translate("+K+","+L+")"}),v.attr("transform","translate("+(d-c.right-N)+","+c.top+")"),e=c.top+c.bottom+L+15}if("furious"==o){w.attr("width",function(a,b){return B[0][b].getComputedTextLength()+27}).attr("height",18).attr("y",-9).attr("x",-15),u.insert("rect",":first-child").attr("class","nv-legend-bg").attr("fill","#eee").attr("opacity",0);var O=v.select(".nv-legend-bg");O.transition().duration(300).attr("x",-x).attr("width",C+x-12).attr("height",e+10).attr("y",-c.top-10).attr("opacity",m?1:0)}w.style("fill",r).style("fill-opacity",s).style("stroke",r)}),b}var c={top:5,right:0,bottom:5,left:0},d=400,e=20,f=function(a){return a.key},g=a.utils.getColor(),h=!0,i=32,j=!0,k=!0,l=!1,m=!1,n=d3.dispatch("legendClick","legendDblclick","legendMouseover","legendMouseout","stateChange"),o="classic";return b.dispatch=n,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},key:{get:function(){return f},set:function(a){f=a}},align:{get:function(){return h},set:function(a){h=a}},rightAlign:{get:function(){return j},set:function(a){j=a}},padding:{get:function(){return i},set:function(a){i=a}},updateState:{get:function(){return k},set:function(a){k=a}},radioButtonMode:{get:function(){return l},set:function(a){l=a}},expanded:{get:function(){return m},set:function(a){m=a}},vers:{get:function(){return o},set:function(a){o=a}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return g},set:function(b){g=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.line=function(){"use strict";function b(r){return v.reset(),v.models(e),r.each(function(b){i=d3.select(this);var r=a.utils.availableWidth(g,i,f),s=a.utils.availableHeight(h,i,f);a.utils.initSVG(i),c=e.xScale(),d=e.yScale(),t=t||c,u=u||d;var w=i.selectAll("g.nv-wrap.nv-line").data([b]),x=w.enter().append("g").attr("class","nvd3 nv-wrap nv-line"),y=x.append("defs"),z=x.append("g"),A=w.select("g");z.append("g").attr("class","nv-groups"),z.append("g").attr("class","nv-scatterWrap"),w.attr("transform","translate("+f.left+","+f.top+")"),e.width(r).height(s);var B=w.select(".nv-scatterWrap");B.call(e),y.append("clipPath").attr("id","nv-edge-clip-"+e.id()).append("rect"),w.select("#nv-edge-clip-"+e.id()+" rect").attr("width",r).attr("height",s>0?s:0),A.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":""),B.attr("clip-path",p?"url(#nv-edge-clip-"+e.id()+")":"");var C=w.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});C.enter().append("g").style("stroke-opacity",1e-6).style("stroke-width",function(a){return a.strokeWidth||j}).style("fill-opacity",1e-6),C.exit().remove(),C.attr("class",function(a,b){return(a.classed||"")+" nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return k(a,b)}).style("stroke",function(a,b){return k(a,b)}),C.watchTransition(v,"line: groups").style("stroke-opacity",1).style("fill-opacity",function(a){return a.fillOpacity||.5});var D=C.selectAll("path.nv-area").data(function(a){return o(a)?[a]:[]});D.enter().append("path").attr("class","nv-area").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y0(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))}).y1(function(){return u(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])}),C.exit().selectAll("path.nv-area").remove(),D.watchTransition(v,"line: areaPaths").attr("d",function(b){return d3.svg.area().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y0(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))}).y1(function(){return d(d.domain()[0]<=0?d.domain()[1]>=0?0:d.domain()[1]:d.domain()[0])}).apply(this,[b.values])});var E=C.selectAll("path.nv-line").data(function(a){return[a.values]});E.enter().append("path").attr("class","nv-line").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,c){return a.utils.NaNtoZero(t(l(b,c)))}).y(function(b,c){return a.utils.NaNtoZero(u(m(b,c)))})),E.watchTransition(v,"line: linePaths").attr("d",d3.svg.line().interpolate(q).defined(n).x(function(b,d){return a.utils.NaNtoZero(c(l(b,d)))}).y(function(b,c){return a.utils.NaNtoZero(d(m(b,c)))})),t=c.copy(),u=d.copy()}),v.renderEnd("line immediate"),b}var c,d,e=a.models.scatter(),f={top:0,right:0,bottom:0,left:0},g=960,h=500,i=null,j=1.5,k=a.utils.defaultColor(),l=function(a){return a.x},m=function(a){return a.y},n=function(a,b){return!isNaN(m(a,b))&&null!==m(a,b)},o=function(a){return a.area},p=!1,q="linear",r=250,s=d3.dispatch("elementClick","elementMouseover","elementMouseout","renderEnd");e.pointSize(16).pointDomain([16,256]);var t,u,v=a.utils.renderWatch(s,r);return b.dispatch=s,b.scatter=e,e.dispatch.on("elementClick",function(){s.elementClick.apply(this,arguments)}),e.dispatch.on("elementMouseover",function(){s.elementMouseover.apply(this,arguments)}),e.dispatch.on("elementMouseout",function(){s.elementMouseout.apply(this,arguments)}),b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},defined:{get:function(){return n},set:function(a){n=a}},interpolate:{get:function(){return q},set:function(a){q=a}},clipEdge:{get:function(){return p},set:function(a){p=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}},duration:{get:function(){return r},set:function(a){r=a,v.reset(r),e.duration(r)}},isArea:{get:function(){return o},set:function(a){o=d3.functor(a)}},x:{get:function(){return l},set:function(a){l=a,e.x(a)}},y:{get:function(){return m},set:function(a){m=a,e.y(a)}},color:{get:function(){return k},set:function(b){k=a.utils.getColor(b),e.color(k)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.lineChart=function(){"use strict";function b(j){return y.reset(),y.models(e),p&&y.models(f),q&&y.models(g),j.each(function(j){var v=d3.select(this),y=this;a.utils.initSVG(v);var B=a.utils.availableWidth(m,v,k),C=a.utils.availableHeight(n,v,k);if(b.update=function(){0===x?v.call(b):v.transition().duration(x).call(b)},b.container=this,t.setter(A(j),b.update).getter(z(j)).update(),t.disabled=j.map(function(a){return!!a.disabled}),!u){var D;u={};for(D in t)u[D]=t[D]instanceof Array?t[D].slice(0):t[D] -}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,v),b;v.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var E=v.selectAll("g.nv-wrap.nv-lineChart").data([j]),F=E.enter().append("g").attr("class","nvd3 nv-wrap nv-lineChart").append("g"),G=E.select("g");F.append("rect").style("opacity",0),F.append("g").attr("class","nv-x nv-axis"),F.append("g").attr("class","nv-y nv-axis"),F.append("g").attr("class","nv-linesWrap"),F.append("g").attr("class","nv-legendWrap"),F.append("g").attr("class","nv-interactive"),G.select("rect").attr("width",B).attr("height",C>0?C:0),o&&(h.width(B),G.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),C=a.utils.availableHeight(n,v,k)),E.select(".nv-legendWrap").attr("transform","translate(0,"+-k.top+")")),E.attr("transform","translate("+k.left+","+k.top+")"),r&&G.select(".nv-y.nv-axis").attr("transform","translate("+B+",0)"),s&&(i.width(B).height(C).margin({left:k.left,top:k.top}).svgContainer(v).xScale(c),E.select(".nv-interactive").call(i)),e.width(B).height(C).color(j.map(function(a,b){return a.color||l(a,b)}).filter(function(a,b){return!j[b].disabled}));var H=G.select(".nv-linesWrap").datum(j.filter(function(a){return!a.disabled}));H.call(e),p&&(f.scale(c)._ticks(a.utils.calcTicksX(B/100,j)).tickSize(-C,0),G.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),G.select(".nv-x.nv-axis").call(f)),q&&(g.scale(d)._ticks(a.utils.calcTicksY(C/36,j)).tickSize(-B,0),G.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)t[c]=a[c];w.stateChange(t),b.update()}),i.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,h,m,n=[];if(j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,g){h=a.interactiveBisect(f.values,c.pointXValue,b.x());var i=f.values[h],j=b.y()(i,h);null!=j&&e.highlightPoint(g,h,!0),void 0!==i&&(void 0===d&&(d=i),void 0===m&&(m=b.xScale()(b.x()(i,h))),n.push({key:f.key,value:j,color:l(f,f.seriesIndex)}))}),n.length>2){var o=b.yScale().invert(c.mouseY),p=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),q=.03*p,r=a.nearestValueIndex(n.map(function(a){return a.value}),o,q);null!==r&&(n[r].highlight=!0)}var s=f.tickFormat()(b.x()(d,h));i.tooltip.position({left:c.mouseX+k.left,top:c.mouseY+k.top}).chartContainer(y.parentNode).valueFormatter(function(a){return null==a?"N/A":g.tickFormat()(a)}).data({value:s,index:h,series:n})(),i.renderGuideLine(m)}),i.dispatch.on("elementClick",function(c){var d,f=[];j.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(e){var g=a.interactiveBisect(e.values,c.pointXValue,b.x()),h=e.values[g];if("undefined"!=typeof h){"undefined"==typeof d&&(d=b.xScale()(b.x()(h,g)));var i=b.yScale()(b.y()(h,g));f.push({point:h,pointIndex:g,pos:[d,i],seriesIndex:e.seriesIndex,series:e})}}),e.dispatch.elementClick(f)}),i.dispatch.on("elementMouseout",function(){e.clearHighlights()}),w.on("changeState",function(a){"undefined"!=typeof a.disabled&&j.length===a.disabled.length&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),t.disabled=a.disabled),b.update()})}),y.renderEnd("lineChart immediate"),b}var c,d,e=a.models.line(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.interactiveGuideline(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=a.utils.defaultColor(),m=null,n=null,o=!0,p=!0,q=!0,r=!1,s=!1,t=a.utils.state(),u=null,v=null,w=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),x=250;f.orient("bottom").tickPadding(7),g.orient(r?"right":"left"),j.valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)});var y=a.utils.renderWatch(w,x),z=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},A=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){j.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),b.dispatch=w,b.lines=e,b.legend=h,b.xAxis=f,b.yAxis=g,b.interactiveLayer=i,b.tooltip=j,b.dispatch=w,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return o},set:function(a){o=a}},showXAxis:{get:function(){return p},set:function(a){p=a}},showYAxis:{get:function(){return q},set:function(a){q=a}},defaultState:{get:function(){return u},set:function(a){u=a}},noData:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return x},set:function(a){x=a,y.reset(x),e.duration(x),f.duration(x),g.duration(x)}},color:{get:function(){return l},set:function(b){l=a.utils.getColor(b),h.color(l),e.color(l)}},rightAlignYAxis:{get:function(){return r},set:function(a){r=a,g.orient(r?"right":"left")}},useInteractiveGuideline:{get:function(){return s},set:function(a){s=a,s&&(e.interactive(!1),e.useVoronoi(!1))}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.linePlusBarChart=function(){"use strict";function b(v){return v.each(function(v){function J(a){var b=+("e"==a),c=b?1:-1,d=X/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function S(){u.empty()||u.extent(I),kb.data([u.empty()?e.domain():I]).each(function(a){var b=e(a[0])-e.range()[0],c=e.range()[1]-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>c?0:c)})}function T(){I=u.empty()?null:u.extent(),c=u.empty()?e.domain():u.extent(),K.brush({extent:c,brush:u}),S(),l.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),j.width(V).height(W).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var b=db.select(".nv-focus .nv-barsWrap").datum(Z.length?Z.map(function(a){return{key:a.key,values:a.values.filter(function(a,b){return l.x()(a,b)>=c[0]&&l.x()(a,b)<=c[1]})}}):[{values:[]}]),h=db.select(".nv-focus .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$.map(function(a){return{area:a.area,fillOpacity:a.fillOpacity,key:a.key,values:a.values.filter(function(a,b){return j.x()(a,b)>=c[0]&&j.x()(a,b)<=c[1]})}}));d=Z.length?l.xScale():j.xScale(),n.scale(d)._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-W,0),n.domain([Math.ceil(c[0]),Math.floor(c[1])]),db.select(".nv-x.nv-axis").transition().duration(L).call(n),b.transition().duration(L).call(l),h.transition().duration(L).call(j),db.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),p.scale(f)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(-V,0),q.scale(g)._ticks(a.utils.calcTicksY(W/36,v)).tickSize(Z.length?0:-V,0),db.select(".nv-focus .nv-y1.nv-axis").style("opacity",Z.length?1:0),db.select(".nv-focus .nv-y2.nv-axis").style("opacity",$.length&&!$[0].disabled?1:0).attr("transform","translate("+d.range()[1]+",0)"),db.select(".nv-focus .nv-y1.nv-axis").transition().duration(L).call(p),db.select(".nv-focus .nv-y2.nv-axis").transition().duration(L).call(q)}var U=d3.select(this);a.utils.initSVG(U);var V=a.utils.availableWidth(y,U,w),W=a.utils.availableHeight(z,U,w)-(E?H:0),X=H-x.top-x.bottom;if(b.update=function(){U.transition().duration(L).call(b)},b.container=this,M.setter(R(v),b.update).getter(Q(v)).update(),M.disabled=v.map(function(a){return!!a.disabled}),!N){var Y;N={};for(Y in M)N[Y]=M[Y]instanceof Array?M[Y].slice(0):M[Y]}if(!(v&&v.length&&v.filter(function(a){return a.values.length}).length))return a.utils.noData(b,U),b;U.selectAll(".nv-noData").remove();var Z=v.filter(function(a){return!a.disabled&&a.bar}),$=v.filter(function(a){return!a.bar});d=l.xScale(),e=o.scale(),f=l.yScale(),g=j.yScale(),h=m.yScale(),i=k.yScale();var _=v.filter(function(a){return!a.disabled&&a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})}),ab=v.filter(function(a){return!a.disabled&&!a.bar}).map(function(a){return a.values.map(function(a,b){return{x:A(a,b),y:B(a,b)}})});d.range([0,V]),e.domain(d3.extent(d3.merge(_.concat(ab)),function(a){return a.x})).range([0,V]);var bb=U.selectAll("g.nv-wrap.nv-linePlusBar").data([v]),cb=bb.enter().append("g").attr("class","nvd3 nv-wrap nv-linePlusBar").append("g"),db=bb.select("g");cb.append("g").attr("class","nv-legendWrap");var eb=cb.append("g").attr("class","nv-focus");eb.append("g").attr("class","nv-x nv-axis"),eb.append("g").attr("class","nv-y1 nv-axis"),eb.append("g").attr("class","nv-y2 nv-axis"),eb.append("g").attr("class","nv-barsWrap"),eb.append("g").attr("class","nv-linesWrap");var fb=cb.append("g").attr("class","nv-context");if(fb.append("g").attr("class","nv-x nv-axis"),fb.append("g").attr("class","nv-y1 nv-axis"),fb.append("g").attr("class","nv-y2 nv-axis"),fb.append("g").attr("class","nv-barsWrap"),fb.append("g").attr("class","nv-linesWrap"),fb.append("g").attr("class","nv-brushBackground"),fb.append("g").attr("class","nv-x nv-brush"),D){var gb=t.align()?V/2:V,hb=t.align()?gb:0;t.width(gb),db.select(".nv-legendWrap").datum(v.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(a.bar?O:P),a})).call(t),w.top!=t.height()&&(w.top=t.height(),W=a.utils.availableHeight(z,U,w)-H),db.select(".nv-legendWrap").attr("transform","translate("+hb+","+-w.top+")")}bb.attr("transform","translate("+w.left+","+w.top+")"),db.select(".nv-context").style("display",E?"initial":"none"),m.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&v[b].bar})),k.width(V).height(X).color(v.map(function(a,b){return a.color||C(a,b)}).filter(function(a,b){return!v[b].disabled&&!v[b].bar}));var ib=db.select(".nv-context .nv-barsWrap").datum(Z.length?Z:[{values:[]}]),jb=db.select(".nv-context .nv-linesWrap").datum($[0].disabled?[{values:[]}]:$);db.select(".nv-context").attr("transform","translate(0,"+(W+w.bottom+x.top)+")"),ib.transition().call(m),jb.transition().call(k),G&&(o._ticks(a.utils.calcTicksX(V/100,v)).tickSize(-X,0),db.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+h.range()[0]+")"),db.select(".nv-context .nv-x.nv-axis").transition().call(o)),F&&(r.scale(h)._ticks(X/36).tickSize(-V,0),s.scale(i)._ticks(X/36).tickSize(Z.length?0:-V,0),db.select(".nv-context .nv-y3.nv-axis").style("opacity",Z.length?1:0).attr("transform","translate(0,"+e.range()[0]+")"),db.select(".nv-context .nv-y2.nv-axis").style("opacity",$.length?1:0).attr("transform","translate("+e.range()[1]+",0)"),db.select(".nv-context .nv-y1.nv-axis").transition().call(r),db.select(".nv-context .nv-y2.nv-axis").transition().call(s)),u.x(e).on("brush",T),I&&u.extent(I);var kb=db.select(".nv-brushBackground").selectAll("g").data([I||u.extent()]),lb=kb.enter().append("g");lb.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",X),lb.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",X);var mb=db.select(".nv-x.nv-brush").call(u);mb.selectAll("rect").attr("height",X),mb.selectAll(".resize").append("path").attr("d",J),t.dispatch.on("stateChange",function(a){for(var c in a)M[c]=a[c];K.stateChange(M),b.update()}),K.on("changeState",function(a){"undefined"!=typeof a.disabled&&(v.forEach(function(b,c){b.disabled=a.disabled[c]}),M.disabled=a.disabled),b.update()}),T()}),b}var c,d,e,f,g,h,i,j=a.models.line(),k=a.models.line(),l=a.models.historicalBar(),m=a.models.historicalBar(),n=a.models.axis(),o=a.models.axis(),p=a.models.axis(),q=a.models.axis(),r=a.models.axis(),s=a.models.axis(),t=a.models.legend(),u=d3.svg.brush(),v=a.models.tooltip(),w={top:30,right:30,bottom:30,left:60},x={top:0,right:30,bottom:20,left:60},y=null,z=null,A=function(a){return a.x},B=function(a){return a.y},C=a.utils.defaultColor(),D=!0,E=!0,F=!1,G=!0,H=50,I=null,J=null,K=d3.dispatch("brush","stateChange","changeState"),L=0,M=a.utils.state(),N=null,O=" (left axis)",P=" (right axis)";j.clipEdge(!0),k.interactive(!1),n.orient("bottom").tickPadding(5),p.orient("left"),q.orient("right"),o.orient("bottom").tickPadding(5),r.orient("left"),s.orient("right"),v.headerEnabled(!0).headerFormatter(function(a,b){return n.tickFormat()(a,b)});var Q=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},R=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return j.dispatch.on("elementMouseover.tooltip",function(a){v.duration(100).valueFormatter(function(a,b){return q.tickFormat()(a,b)}).data(a).position(a.pos).hidden(!1)}),j.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={value:b.y()(a.data),color:a.color},v.duration(0).valueFormatter(function(a,b){return p.tickFormat()(a,b)}).data(a).hidden(!1)}),l.dispatch.on("elementMouseout.tooltip",function(){v.hidden(!0)}),l.dispatch.on("elementMousemove.tooltip",function(){v.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=K,b.legend=t,b.lines=j,b.lines2=k,b.bars=l,b.bars2=m,b.xAxis=n,b.x2Axis=o,b.y1Axis=p,b.y2Axis=q,b.y3Axis=r,b.y4Axis=s,b.tooltip=v,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return y},set:function(a){y=a}},height:{get:function(){return z},set:function(a){z=a}},showLegend:{get:function(){return D},set:function(a){D=a}},brushExtent:{get:function(){return I},set:function(a){I=a}},noData:{get:function(){return J},set:function(a){J=a}},focusEnable:{get:function(){return E},set:function(a){E=a}},focusHeight:{get:function(){return H},set:function(a){H=a}},focusShowAxisX:{get:function(){return G},set:function(a){G=a}},focusShowAxisY:{get:function(){return F},set:function(a){F=a}},legendLeftAxisHint:{get:function(){return O},set:function(a){O=a}},legendRightAxisHint:{get:function(){return P},set:function(a){P=a}},tooltips:{get:function(){return v.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),v.enabled(!!b)}},tooltipContent:{get:function(){return v.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),v.contentGenerator(b)}},margin:{get:function(){return w},set:function(a){w.top=void 0!==a.top?a.top:w.top,w.right=void 0!==a.right?a.right:w.right,w.bottom=void 0!==a.bottom?a.bottom:w.bottom,w.left=void 0!==a.left?a.left:w.left}},duration:{get:function(){return L},set:function(a){L=a}},color:{get:function(){return C},set:function(b){C=a.utils.getColor(b),t.color(C)}},x:{get:function(){return A},set:function(a){A=a,j.x(a),k.x(a),l.x(a),m.x(a)}},y:{get:function(){return B},set:function(a){B=a,j.y(a),k.y(a),l.y(a),m.y(a)}}}),a.utils.inheritOptions(b,j),a.utils.initOptions(b),b},a.models.lineWithFocusChart=function(){"use strict";function b(o){return o.each(function(o){function z(a){var b=+("e"==a),c=b?1:-1,d=M/3;return"M"+.5*c+","+d+"A6,6 0 0 "+b+" "+6.5*c+","+(d+6)+"V"+(2*d-6)+"A6,6 0 0 "+b+" "+.5*c+","+2*d+"ZM"+2.5*c+","+(d+8)+"V"+(2*d-8)+"M"+4.5*c+","+(d+8)+"V"+(2*d-8)}function G(){n.empty()||n.extent(y),U.data([n.empty()?e.domain():y]).each(function(a){var b=e(a[0])-c.range()[0],d=K-e(a[1]);d3.select(this).select(".left").attr("width",0>b?0:b),d3.select(this).select(".right").attr("x",e(a[1])).attr("width",0>d?0:d)})}function H(){y=n.empty()?null:n.extent();var a=n.empty()?e.domain():n.extent();if(!(Math.abs(a[0]-a[1])<=1)){A.brush({extent:a,brush:n}),G();var b=Q.select(".nv-focus .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}).map(function(b){return{key:b.key,area:b.area,values:b.values.filter(function(b,c){return g.x()(b,c)>=a[0]&&g.x()(b,c)<=a[1]})}}));b.transition().duration(B).call(g),Q.select(".nv-focus .nv-x.nv-axis").transition().duration(B).call(i),Q.select(".nv-focus .nv-y.nv-axis").transition().duration(B).call(j)}}var I=d3.select(this),J=this;a.utils.initSVG(I);var K=a.utils.availableWidth(t,I,q),L=a.utils.availableHeight(u,I,q)-v,M=v-r.top-r.bottom;if(b.update=function(){I.transition().duration(B).call(b)},b.container=this,C.setter(F(o),b.update).getter(E(o)).update(),C.disabled=o.map(function(a){return!!a.disabled}),!D){var N;D={};for(N in C)D[N]=C[N]instanceof Array?C[N].slice(0):C[N]}if(!(o&&o.length&&o.filter(function(a){return a.values.length}).length))return a.utils.noData(b,I),b;I.selectAll(".nv-noData").remove(),c=g.xScale(),d=g.yScale(),e=h.xScale(),f=h.yScale();var O=I.selectAll("g.nv-wrap.nv-lineWithFocusChart").data([o]),P=O.enter().append("g").attr("class","nvd3 nv-wrap nv-lineWithFocusChart").append("g"),Q=O.select("g");P.append("g").attr("class","nv-legendWrap");var R=P.append("g").attr("class","nv-focus");R.append("g").attr("class","nv-x nv-axis"),R.append("g").attr("class","nv-y nv-axis"),R.append("g").attr("class","nv-linesWrap"),R.append("g").attr("class","nv-interactive");var S=P.append("g").attr("class","nv-context");S.append("g").attr("class","nv-x nv-axis"),S.append("g").attr("class","nv-y nv-axis"),S.append("g").attr("class","nv-linesWrap"),S.append("g").attr("class","nv-brushBackground"),S.append("g").attr("class","nv-x nv-brush"),x&&(m.width(K),Q.select(".nv-legendWrap").datum(o).call(m),q.top!=m.height()&&(q.top=m.height(),L=a.utils.availableHeight(u,I,q)-v),Q.select(".nv-legendWrap").attr("transform","translate(0,"+-q.top+")")),O.attr("transform","translate("+q.left+","+q.top+")"),w&&(p.width(K).height(L).margin({left:q.left,top:q.top}).svgContainer(I).xScale(c),O.select(".nv-interactive").call(p)),g.width(K).height(L).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),h.defined(g.defined()).width(K).height(M).color(o.map(function(a,b){return a.color||s(a,b)}).filter(function(a,b){return!o[b].disabled})),Q.select(".nv-context").attr("transform","translate(0,"+(L+q.bottom+r.top)+")");var T=Q.select(".nv-context .nv-linesWrap").datum(o.filter(function(a){return!a.disabled}));d3.transition(T).call(h),i.scale(c)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-L,0),j.scale(d)._ticks(a.utils.calcTicksY(L/36,o)).tickSize(-K,0),Q.select(".nv-focus .nv-x.nv-axis").attr("transform","translate(0,"+L+")"),n.x(e).on("brush",function(){H()}),y&&n.extent(y);var U=Q.select(".nv-brushBackground").selectAll("g").data([y||n.extent()]),V=U.enter().append("g");V.append("rect").attr("class","left").attr("x",0).attr("y",0).attr("height",M),V.append("rect").attr("class","right").attr("x",0).attr("y",0).attr("height",M);var W=Q.select(".nv-x.nv-brush").call(n);W.selectAll("rect").attr("height",M),W.selectAll(".resize").append("path").attr("d",z),H(),k.scale(e)._ticks(a.utils.calcTicksX(K/100,o)).tickSize(-M,0),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),d3.transition(Q.select(".nv-context .nv-x.nv-axis")).call(k),l.scale(f)._ticks(a.utils.calcTicksY(M/36,o)).tickSize(-K,0),d3.transition(Q.select(".nv-context .nv-y.nv-axis")).call(l),Q.select(".nv-context .nv-x.nv-axis").attr("transform","translate(0,"+f.range()[0]+")"),m.dispatch.on("stateChange",function(a){for(var c in a)C[c]=a[c];A.stateChange(C),b.update()}),p.dispatch.on("elementMousemove",function(c){g.clearHighlights();var d,f,h,k=[];if(o.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(i,j){var l=n.empty()?e.domain():n.extent(),m=i.values.filter(function(a,b){return g.x()(a,b)>=l[0]&&g.x()(a,b)<=l[1]});f=a.interactiveBisect(m,c.pointXValue,g.x());var o=m[f],p=b.y()(o,f);null!=p&&g.highlightPoint(j,f,!0),void 0!==o&&(void 0===d&&(d=o),void 0===h&&(h=b.xScale()(b.x()(o,f))),k.push({key:i.key,value:b.y()(o,f),color:s(i,i.seriesIndex)}))}),k.length>2){var l=b.yScale().invert(c.mouseY),m=Math.abs(b.yScale().domain()[0]-b.yScale().domain()[1]),r=.03*m,t=a.nearestValueIndex(k.map(function(a){return a.value}),l,r);null!==t&&(k[t].highlight=!0)}var u=i.tickFormat()(b.x()(d,f));p.tooltip.position({left:c.mouseX+q.left,top:c.mouseY+q.top}).chartContainer(J.parentNode).valueFormatter(function(a){return null==a?"N/A":j.tickFormat()(a)}).data({value:u,index:f,series:k})(),p.renderGuideLine(h)}),p.dispatch.on("elementMouseout",function(){g.clearHighlights()}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&o.forEach(function(b,c){b.disabled=a.disabled[c]}),b.update()})}),b}var c,d,e,f,g=a.models.line(),h=a.models.line(),i=a.models.axis(),j=a.models.axis(),k=a.models.axis(),l=a.models.axis(),m=a.models.legend(),n=d3.svg.brush(),o=a.models.tooltip(),p=a.interactiveGuideline(),q={top:30,right:30,bottom:30,left:60},r={top:0,right:30,bottom:20,left:60},s=a.utils.defaultColor(),t=null,u=null,v=50,w=!1,x=!0,y=null,z=null,A=d3.dispatch("brush","stateChange","changeState"),B=250,C=a.utils.state(),D=null;g.clipEdge(!0).duration(0),h.interactive(!1),i.orient("bottom").tickPadding(5),j.orient("left"),k.orient("bottom").tickPadding(5),l.orient("left"),o.valueFormatter(function(a,b){return j.tickFormat()(a,b)}).headerFormatter(function(a,b){return i.tickFormat()(a,b)});var E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return g.dispatch.on("elementMouseover.tooltip",function(a){o.data(a).position(a.pos).hidden(!1)}),g.dispatch.on("elementMouseout.tooltip",function(){o.hidden(!0)}),b.dispatch=A,b.legend=m,b.lines=g,b.lines2=h,b.xAxis=i,b.yAxis=j,b.x2Axis=k,b.y2Axis=l,b.interactiveLayer=p,b.tooltip=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return t},set:function(a){t=a}},height:{get:function(){return u},set:function(a){u=a}},focusHeight:{get:function(){return v},set:function(a){v=a}},showLegend:{get:function(){return x},set:function(a){x=a}},brushExtent:{get:function(){return y},set:function(a){y=a}},defaultState:{get:function(){return D},set:function(a){D=a}},noData:{get:function(){return z},set:function(a){z=a}},tooltips:{get:function(){return o.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),o.enabled(!!b)}},tooltipContent:{get:function(){return o.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),o.contentGenerator(b)}},margin:{get:function(){return q},set:function(a){q.top=void 0!==a.top?a.top:q.top,q.right=void 0!==a.right?a.right:q.right,q.bottom=void 0!==a.bottom?a.bottom:q.bottom,q.left=void 0!==a.left?a.left:q.left}},color:{get:function(){return s},set:function(b){s=a.utils.getColor(b),m.color(s)}},interpolate:{get:function(){return g.interpolate()},set:function(a){g.interpolate(a),h.interpolate(a)}},xTickFormat:{get:function(){return i.tickFormat()},set:function(a){i.tickFormat(a),k.tickFormat(a)}},yTickFormat:{get:function(){return j.tickFormat()},set:function(a){j.tickFormat(a),l.tickFormat(a)}},duration:{get:function(){return B},set:function(a){B=a,j.duration(B),l.duration(B),i.duration(B),k.duration(B)}},x:{get:function(){return g.x()},set:function(a){g.x(a),h.x(a)}},y:{get:function(){return g.y()},set:function(a){g.y(a),h.y(a)}},useInteractiveGuideline:{get:function(){return w},set:function(a){w=a,w&&(g.interactive(!1),g.useVoronoi(!1))}}}),a.utils.inheritOptions(b,g),a.utils.initOptions(b),b},a.models.multiBar=function(){"use strict";function b(E){return C.reset(),E.each(function(b){var E=k-j.left-j.right,F=l-j.top-j.bottom;p=d3.select(this),a.utils.initSVG(p);var G=0;if(x&&b.length&&(x=[{values:b[0].values.map(function(a){return{x:a.x,y:0,series:a.series,size:.01}})}]),u){var H=d3.layout.stack().offset(v).values(function(a){return a.values}).y(r)(!b.length&&x?x:b);H.forEach(function(a,c){a.nonStackable?(b[c].nonStackableSeries=G++,H[c]=b[c]):c>0&&H[c-1].nonStackable&&H[c].values.map(function(a,b){a.y0-=H[c-1].values[b].y,a.y1=a.y0+a.y})}),b=H}b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),u&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a,f){if(!b[f].nonStackable){var g=a.values[c];g.size=Math.abs(g.y),g.y<0?(g.y1=e,e-=g.size):(g.y1=g.size+d,d+=g.size)}})});var I=d&&e?[]:b.map(function(a,b){return a.values.map(function(a,c){return{x:q(a,c),y:r(a,c),y0:a.y0,y1:a.y1,idx:b}})});m.domain(d||d3.merge(I).map(function(a){return a.x})).rangeBands(f||[0,E],A),n.domain(e||d3.extent(d3.merge(I).map(function(a){var c=a.y;return u&&!b[a.idx].nonStackable&&(c=a.y>0?a.y1:a.y1+a.y),c}).concat(s))).range(g||[F,0]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]+.01*n.domain()[0],n.domain()[1]-.01*n.domain()[1]]:[-1,1]),h=h||m,i=i||n;var J=p.selectAll("g.nv-wrap.nv-multibar").data([b]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multibar"),L=K.append("defs"),M=K.append("g"),N=J.select("g");M.append("g").attr("class","nv-groups"),J.attr("transform","translate("+j.left+","+j.top+")"),L.append("clipPath").attr("id","nv-edge-clip-"+o).append("rect"),J.select("#nv-edge-clip-"+o+" rect").attr("width",E).attr("height",F),N.attr("clip-path",t?"url(#nv-edge-clip-"+o+")":"");var O=J.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});O.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6);var P=C.transition(O.exit().selectAll("rect.nv-bar"),"multibarExit",Math.min(100,z)).attr("y",function(a){var c=i(0)||0;return u&&b[a.series]&&!b[a.series].nonStackable&&(c=i(a.y0)),c}).attr("height",0).remove();P.delay&&P.delay(function(a,b){var c=b*(z/(D+1))-b;return c}),O.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return w(a,b)}).style("stroke",function(a,b){return w(a,b)}),O.style("stroke-opacity",1).style("fill-opacity",.75);var Q=O.selectAll("rect.nv-bar").data(function(a){return x&&!b.length?x.values:a.values});Q.exit().remove();Q.enter().append("rect").attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("x",function(a,c,d){return u&&!b[d].nonStackable?0:d*m.rangeBand()/b.length}).attr("y",function(a,c,d){return i(u&&!b[d].nonStackable?a.y0:0)||0}).attr("height",0).attr("width",function(a,c,d){return m.rangeBand()/(u&&!b[d].nonStackable?1:b.length)}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"});Q.style("fill",function(a,b,c){return w(a,c,b)}).style("stroke",function(a,b,c){return w(a,c,b)}).on("mouseover",function(a,b){d3.select(this).classed("hover",!0),B.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),B.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){B.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){B.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){B.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),Q.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}).attr("transform",function(a,b){return"translate("+m(q(a,b))+",0)"}),y&&(c||(c=b.map(function(){return!0})),Q.style("fill",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(y(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}));var R=Q.watchTransition(C,"multibar",Math.min(250,z)).delay(function(a,c){return c*z/b[0].values.length});u?R.attr("y",function(a,c,d){var e=0;return e=b[d].nonStackable?r(a,c)<0?n(0):n(0)-n(r(a,c))<-1?n(0)-1:n(r(a,c))||0:n(a.y1)}).attr("height",function(a,c,d){return b[d].nonStackable?Math.max(Math.abs(n(r(a,c))-n(0)),1)||0:Math.max(Math.abs(n(a.y+a.y0)-n(a.y0)),1)}).attr("x",function(a,c,d){var e=0;return b[d].nonStackable&&(e=a.series*m.rangeBand()/b.length,b.length!==G&&(e=b[d].nonStackableSeries*m.rangeBand()/(2*G))),e}).attr("width",function(a,c,d){if(b[d].nonStackable){var e=m.rangeBand()/G;return b.length!==G&&(e=m.rangeBand()/(2*G)),e}return m.rangeBand()}):R.attr("x",function(a){return a.series*m.rangeBand()/b.length}).attr("width",m.rangeBand()/b.length).attr("y",function(a,b){return r(a,b)<0?n(0):n(0)-n(r(a,b))<1?n(0)-1:n(r(a,b))||0}).attr("height",function(a,b){return Math.max(Math.abs(n(r(a,b))-n(0)),1)||0}),h=m.copy(),i=n.copy(),b[0]&&b[0].values&&(D=b[0].values.length)}),C.renderEnd("multibar immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=d3.scale.ordinal(),n=d3.scale.linear(),o=Math.floor(1e4*Math.random()),p=null,q=function(a){return a.x},r=function(a){return a.y},s=[0],t=!0,u=!1,v="zero",w=a.utils.defaultColor(),x=!1,y=null,z=500,A=.1,B=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),C=a.utils.renderWatch(B,z),D=0;return b.dispatch=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return s},set:function(a){s=a}},stacked:{get:function(){return u},set:function(a){u=a}},stackOffset:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return t},set:function(a){t=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return o},set:function(a){o=a}},hideable:{get:function(){return x},set:function(a){x=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z)}},color:{get:function(){return w},set:function(b){w=a.utils.getColor(b)}},barColor:{get:function(){return y},set:function(b){y=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarChart=function(){"use strict";function b(j){return D.reset(),D.models(e),r&&D.models(f),s&&D.models(g),j.each(function(j){var z=d3.select(this);a.utils.initSVG(z);var D=a.utils.availableWidth(l,z,k),H=a.utils.availableHeight(m,z,k);if(b.update=function(){0===C?z.call(b):z.transition().duration(C).call(b)},b.container=this,x.setter(G(j),b.update).getter(F(j)).update(),x.disabled=j.map(function(a){return!!a.disabled}),!y){var I;y={};for(I in x)y[I]=x[I]instanceof Array?x[I].slice(0):x[I]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,z),b;z.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale(); -var J=z.selectAll("g.nv-wrap.nv-multiBarWithLegend").data([j]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarWithLegend").append("g"),L=J.select("g");if(K.append("g").attr("class","nv-x nv-axis"),K.append("g").attr("class","nv-y nv-axis"),K.append("g").attr("class","nv-barsWrap"),K.append("g").attr("class","nv-legendWrap"),K.append("g").attr("class","nv-controlsWrap"),q&&(h.width(D-B()),L.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),H=a.utils.availableHeight(m,z,k)),L.select(".nv-legendWrap").attr("transform","translate("+B()+","+-k.top+")")),o){var M=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(B()).color(["#444","#444","#444"]),L.select(".nv-controlsWrap").datum(M).attr("transform","translate(0,"+-k.top+")").call(i)}J.attr("transform","translate("+k.left+","+k.top+")"),t&&L.select(".nv-y.nv-axis").attr("transform","translate("+D+",0)"),e.disabled(j.map(function(a){return a.disabled})).width(D).height(H).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var N=L.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(N.call(e),r){f.scale(c)._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-H,0),L.select(".nv-x.nv-axis").attr("transform","translate(0,"+d.range()[0]+")"),L.select(".nv-x.nv-axis").call(f);var O=L.select(".nv-x.nv-axis > g").selectAll("g");if(O.selectAll("line, text").style("opacity",1),v){var P=function(a,b){return"translate("+a+","+b+")"},Q=5,R=17;O.selectAll("text").attr("transform",function(a,b,c){return P(0,c%2==0?Q:R)});var S=d3.selectAll(".nv-x.nv-axis .nv-wrap g g text")[0].length;L.selectAll(".nv-x.nv-axis .nv-axisMaxMin text").attr("transform",function(a,b){return P(0,0===b||S%2!==0?R:Q)})}u&&O.filter(function(a,b){return b%Math.ceil(j[0].values.length/(D/100))!==0}).selectAll("text, line").style("opacity",0),w&&O.selectAll(".tick text").attr("transform","rotate("+w+" 0,0)").style("text-anchor",w>0?"start":"end"),L.select(".nv-x.nv-axis").selectAll("g.nv-axisMaxMin text").style("opacity",1)}s&&(g.scale(d)._ticks(a.utils.calcTicksY(H/36,j)).tickSize(-D,0),L.select(".nv-y.nv-axis").call(g)),h.dispatch.on("stateChange",function(a){for(var c in a)x[c]=a[c];A.stateChange(x),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(M=M.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":case p.grouped:e.stacked(!1);break;case"Stacked":case p.stacked:e.stacked(!0)}x.stacked=e.stacked(),A.stateChange(x),b.update()}}),A.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),x.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),x.stacked=a.stacked,E=a.stacked),b.update()})}),D.renderEnd("multibarchart immediate"),b}var c,d,e=a.models.multiBar(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=!0,v=!1,w=0,x=a.utils.state(),y=null,z=null,A=d3.dispatch("stateChange","changeState","renderEnd"),B=function(){return o?180:0},C=250;x.stacked=!1,e.stacked(!1),f.orient("bottom").tickPadding(7).showMaxMin(!1).tickFormat(function(a){return a}),g.orient(t?"right":"left").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var D=a.utils.renderWatch(A),E=!1,F=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:E}}},G=function(a){return function(b){void 0!==b.stacked&&(E=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=A,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=x,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return y},set:function(a){y=a}},noData:{get:function(){return z},set:function(a){z=a}},reduceXTicks:{get:function(){return u},set:function(a){u=a}},rotateLabels:{get:function(){return w},set:function(a){w=a}},staggerLabels:{get:function(){return v},set:function(a){v=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return C},set:function(a){C=a,e.duration(C),f.duration(C),g.duration(C),D.reset(C)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiBarHorizontal=function(){"use strict";function b(m){return E.reset(),m.each(function(b){var m=k-j.left-j.right,C=l-j.top-j.bottom;n=d3.select(this),a.utils.initSVG(n),w&&(b=d3.layout.stack().offset("zero").values(function(a){return a.values}).y(r)(b)),b.forEach(function(a,b){a.values.forEach(function(c){c.series=b,c.key=a.key})}),w&&b[0].values.map(function(a,c){var d=0,e=0;b.map(function(a){var b=a.values[c];b.size=Math.abs(b.y),b.y<0?(b.y1=e-b.size,e-=b.size):(b.y1=d,d+=b.size)})});var F=d&&e?[]:b.map(function(a){return a.values.map(function(a,b){return{x:q(a,b),y:r(a,b),y0:a.y0,y1:a.y1}})});o.domain(d||d3.merge(F).map(function(a){return a.x})).rangeBands(f||[0,C],A),p.domain(e||d3.extent(d3.merge(F).map(function(a){return w?a.y>0?a.y1+a.y:a.y1:a.y}).concat(t))),p.range(x&&!w?g||[p.domain()[0]<0?z:0,m-(p.domain()[1]>0?z:0)]:g||[0,m]),h=h||o,i=i||d3.scale.linear().domain(p.domain()).range([p(0),p(0)]);{var G=d3.select(this).selectAll("g.nv-wrap.nv-multibarHorizontal").data([b]),H=G.enter().append("g").attr("class","nvd3 nv-wrap nv-multibarHorizontal"),I=(H.append("defs"),H.append("g"));G.select("g")}I.append("g").attr("class","nv-groups"),G.attr("transform","translate("+j.left+","+j.top+")");var J=G.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a,b){return b});J.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),J.exit().watchTransition(E,"multibarhorizontal: exit groups").style("stroke-opacity",1e-6).style("fill-opacity",1e-6).remove(),J.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}).style("fill",function(a,b){return u(a,b)}).style("stroke",function(a,b){return u(a,b)}),J.watchTransition(E,"multibarhorizontal: groups").style("stroke-opacity",1).style("fill-opacity",.75);var K=J.selectAll("g.nv-bar").data(function(a){return a.values});K.exit().remove();var L=K.enter().append("g").attr("transform",function(a,c,d){return"translate("+i(w?a.y0:0)+","+(w?0:d*o.rangeBand()/b.length+o(q(a,c)))+")"});L.append("rect").attr("width",0).attr("height",o.rangeBand()/(w?1:b.length)),K.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),D.elementMouseover({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){d3.select(this).classed("hover",!1),D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mouseout",function(a,b){D.elementMouseout({data:a,index:b,color:d3.select(this).style("fill")})}).on("mousemove",function(a,b){D.elementMousemove({data:a,index:b,color:d3.select(this).style("fill")})}).on("click",function(a,b){D.elementClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}).on("dblclick",function(a,b){D.elementDblClick({data:a,index:b,color:d3.select(this).style("fill")}),d3.event.stopPropagation()}),s(b[0],0)&&(L.append("polyline"),K.select("polyline").attr("fill","none").attr("points",function(a,c){var d=s(a,c),e=.8*o.rangeBand()/(2*(w?1:b.length));d=d.length?d:[-Math.abs(d),Math.abs(d)],d=d.map(function(a){return p(a)-p(0)});var f=[[d[0],-e],[d[0],e],[d[0],0],[d[1],0],[d[1],-e],[d[1],e]];return f.map(function(a){return a.join(",")}).join(" ")}).attr("transform",function(a,c){var d=o.rangeBand()/(2*(w?1:b.length));return"translate("+(r(a,c)<0?0:p(r(a,c))-p(0))+", "+d+")"})),L.append("text"),x&&!w?(K.select("text").attr("text-anchor",function(a,b){return r(a,b)<0?"end":"start"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){var c=B(r(a,b)),d=s(a,b);return void 0===d?c:d.length?c+"+"+B(Math.abs(d[1]))+"-"+B(Math.abs(d[0])):c+"±"+B(Math.abs(d))}),K.watchTransition(E,"multibarhorizontal: bars").select("text").attr("x",function(a,b){return r(a,b)<0?-4:p(r(a,b))-p(0)+4})):K.selectAll("text").text(""),y&&!w?(L.append("text").classed("nv-bar-label",!0),K.select("text.nv-bar-label").attr("text-anchor",function(a,b){return r(a,b)<0?"start":"end"}).attr("y",o.rangeBand()/(2*b.length)).attr("dy",".32em").text(function(a,b){return q(a,b)}),K.watchTransition(E,"multibarhorizontal: bars").select("text.nv-bar-label").attr("x",function(a,b){return r(a,b)<0?p(0)-p(r(a,b))+4:-4})):K.selectAll("text.nv-bar-label").text(""),K.attr("class",function(a,b){return r(a,b)<0?"nv-bar negative":"nv-bar positive"}),v&&(c||(c=b.map(function(){return!0})),K.style("fill",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()}).style("stroke",function(a,b,d){return d3.rgb(v(a,b)).darker(c.map(function(a,b){return b}).filter(function(a,b){return!c[b]})[d]).toString()})),w?K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,b){return"translate("+p(a.y1)+","+o(q(a,b))+")"}).select("rect").attr("width",function(a,b){return Math.abs(p(r(a,b)+a.y0)-p(a.y0))}).attr("height",o.rangeBand()):K.watchTransition(E,"multibarhorizontal: bars").attr("transform",function(a,c){return"translate("+p(r(a,c)<0?r(a,c):0)+","+(a.series*o.rangeBand()/b.length+o(q(a,c)))+")"}).select("rect").attr("height",o.rangeBand()/b.length).attr("width",function(a,b){return Math.max(Math.abs(p(r(a,b))-p(0)),1)}),h=o.copy(),i=p.copy()}),E.renderEnd("multibarHorizontal immediate"),b}var c,d,e,f,g,h,i,j={top:0,right:0,bottom:0,left:0},k=960,l=500,m=Math.floor(1e4*Math.random()),n=null,o=d3.scale.ordinal(),p=d3.scale.linear(),q=function(a){return a.x},r=function(a){return a.y},s=function(a){return a.yErr},t=[0],u=a.utils.defaultColor(),v=null,w=!1,x=!1,y=!1,z=60,A=.1,B=d3.format(",.2f"),C=250,D=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),E=a.utils.renderWatch(D,C);return b.dispatch=D,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},x:{get:function(){return q},set:function(a){q=a}},y:{get:function(){return r},set:function(a){r=a}},yErr:{get:function(){return s},set:function(a){s=a}},xScale:{get:function(){return o},set:function(a){o=a}},yScale:{get:function(){return p},set:function(a){p=a}},xDomain:{get:function(){return d},set:function(a){d=a}},yDomain:{get:function(){return e},set:function(a){e=a}},xRange:{get:function(){return f},set:function(a){f=a}},yRange:{get:function(){return g},set:function(a){g=a}},forceY:{get:function(){return t},set:function(a){t=a}},stacked:{get:function(){return w},set:function(a){w=a}},showValues:{get:function(){return x},set:function(a){x=a}},disabled:{get:function(){return c},set:function(a){c=a}},id:{get:function(){return m},set:function(a){m=a}},valueFormat:{get:function(){return B},set:function(a){B=a}},valuePadding:{get:function(){return z},set:function(a){z=a}},groupSpacing:{get:function(){return A},set:function(a){A=a}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},duration:{get:function(){return C},set:function(a){C=a,E.reset(C)}},color:{get:function(){return u},set:function(b){u=a.utils.getColor(b)}},barColor:{get:function(){return v},set:function(b){v=b?a.utils.getColor(b):null}}}),a.utils.initOptions(b),b},a.models.multiBarHorizontalChart=function(){"use strict";function b(j){return C.reset(),C.models(e),r&&C.models(f),s&&C.models(g),j.each(function(j){var w=d3.select(this);a.utils.initSVG(w);var C=a.utils.availableWidth(l,w,k),D=a.utils.availableHeight(m,w,k);if(b.update=function(){w.transition().duration(z).call(b)},b.container=this,t=e.stacked(),u.setter(B(j),b.update).getter(A(j)).update(),u.disabled=j.map(function(a){return!!a.disabled}),!v){var E;v={};for(E in u)v[E]=u[E]instanceof Array?u[E].slice(0):u[E]}if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,w),b;w.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var F=w.selectAll("g.nv-wrap.nv-multiBarHorizontalChart").data([j]),G=F.enter().append("g").attr("class","nvd3 nv-wrap nv-multiBarHorizontalChart").append("g"),H=F.select("g");if(G.append("g").attr("class","nv-x nv-axis"),G.append("g").attr("class","nv-y nv-axis").append("g").attr("class","nv-zeroLine").append("line"),G.append("g").attr("class","nv-barsWrap"),G.append("g").attr("class","nv-legendWrap"),G.append("g").attr("class","nv-controlsWrap"),q&&(h.width(C-y()),H.select(".nv-legendWrap").datum(j).call(h),k.top!=h.height()&&(k.top=h.height(),D=a.utils.availableHeight(m,w,k)),H.select(".nv-legendWrap").attr("transform","translate("+y()+","+-k.top+")")),o){var I=[{key:p.grouped||"Grouped",disabled:e.stacked()},{key:p.stacked||"Stacked",disabled:!e.stacked()}];i.width(y()).color(["#444","#444","#444"]),H.select(".nv-controlsWrap").datum(I).attr("transform","translate(0,"+-k.top+")").call(i)}F.attr("transform","translate("+k.left+","+k.top+")"),e.disabled(j.map(function(a){return a.disabled})).width(C).height(D).color(j.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!j[b].disabled}));var J=H.select(".nv-barsWrap").datum(j.filter(function(a){return!a.disabled}));if(J.transition().call(e),r){f.scale(c)._ticks(a.utils.calcTicksY(D/24,j)).tickSize(-C,0),H.select(".nv-x.nv-axis").call(f);var K=H.select(".nv-x.nv-axis").selectAll("g");K.selectAll("line, text")}s&&(g.scale(d)._ticks(a.utils.calcTicksX(C/100,j)).tickSize(-D,0),H.select(".nv-y.nv-axis").attr("transform","translate(0,"+D+")"),H.select(".nv-y.nv-axis").call(g)),H.select(".nv-zeroLine line").attr("x1",d(0)).attr("x2",d(0)).attr("y1",0).attr("y2",-D),h.dispatch.on("stateChange",function(a){for(var c in a)u[c]=a[c];x.stateChange(u),b.update()}),i.dispatch.on("legendClick",function(a){if(a.disabled){switch(I=I.map(function(a){return a.disabled=!0,a}),a.disabled=!1,a.key){case"Grouped":e.stacked(!1);break;case"Stacked":e.stacked(!0)}u.stacked=e.stacked(),x.stateChange(u),t=e.stacked(),b.update()}}),x.on("changeState",function(a){"undefined"!=typeof a.disabled&&(j.forEach(function(b,c){b.disabled=a.disabled[c]}),u.disabled=a.disabled),"undefined"!=typeof a.stacked&&(e.stacked(a.stacked),u.stacked=a.stacked,t=a.stacked),b.update()})}),C.renderEnd("multibar horizontal chart immediate"),b}var c,d,e=a.models.multiBarHorizontal(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend().height(30),i=a.models.legend().height(30),j=a.models.tooltip(),k={top:30,right:20,bottom:50,left:60},l=null,m=null,n=a.utils.defaultColor(),o=!0,p={},q=!0,r=!0,s=!0,t=!1,u=a.utils.state(),v=null,w=null,x=d3.dispatch("stateChange","changeState","renderEnd"),y=function(){return o?180:0},z=250;u.stacked=!1,e.stacked(t),f.orient("left").tickPadding(5).showMaxMin(!1).tickFormat(function(a){return a}),g.orient("bottom").tickFormat(d3.format(",.1f")),j.duration(0).valueFormatter(function(a,b){return g.tickFormat()(a,b)}).headerFormatter(function(a,b){return f.tickFormat()(a,b)}),i.updateState(!1);var A=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),stacked:t}}},B=function(a){return function(b){void 0!==b.stacked&&(t=b.stacked),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},C=a.utils.renderWatch(x,z);return e.dispatch.on("elementMouseover.tooltip",function(a){a.value=b.x()(a.data),a.series={key:a.data.key,value:b.y()(a.data),color:a.color},j.data(a).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){j.hidden(!0)}),e.dispatch.on("elementMousemove.tooltip",function(){j.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=x,b.multibar=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.state=u,b.tooltip=j,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return l},set:function(a){l=a}},height:{get:function(){return m},set:function(a){m=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showControls:{get:function(){return o},set:function(a){o=a}},controlLabels:{get:function(){return p},set:function(a){p=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return v},set:function(a){v=a}},noData:{get:function(){return w},set:function(a){w=a}},tooltips:{get:function(){return j.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),j.enabled(!!b)}},tooltipContent:{get:function(){return j.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),j.contentGenerator(b)}},margin:{get:function(){return k},set:function(a){k.top=void 0!==a.top?a.top:k.top,k.right=void 0!==a.right?a.right:k.right,k.bottom=void 0!==a.bottom?a.bottom:k.bottom,k.left=void 0!==a.left?a.left:k.left}},duration:{get:function(){return z},set:function(a){z=a,C.reset(z),e.duration(z),f.duration(z),g.duration(z)}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),h.color(n)}},barColor:{get:function(){return e.barColor},set:function(a){e.barColor(a),h.color(function(a,b){return d3.rgb("#ccc").darker(1.5*b).toString()})}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.multiChart=function(){"use strict";function b(j){return j.each(function(j){function k(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.value=a.point.x,a.series={value:a.point.y,color:a.point.color},B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function l(a){var b=2===j[a.seriesIndex].yAxis?z:y;a.point.x=v.x()(a.point),a.point.y=v.y()(a.point),B.duration(100).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).position(a.pos).hidden(!1)}function n(a){var b=2===j[a.data.series].yAxis?z:y;a.value=t.x()(a.data),a.series={value:t.y()(a.data),color:a.color},B.duration(0).valueFormatter(function(a,c){return b.tickFormat()(a,c)}).data(a).hidden(!1)}var C=d3.select(this);a.utils.initSVG(C),b.update=function(){C.transition().call(b)},b.container=this;var D=a.utils.availableWidth(g,C,e),E=a.utils.availableHeight(h,C,e),F=j.filter(function(a){return"line"==a.type&&1==a.yAxis}),G=j.filter(function(a){return"line"==a.type&&2==a.yAxis}),H=j.filter(function(a){return"bar"==a.type&&1==a.yAxis}),I=j.filter(function(a){return"bar"==a.type&&2==a.yAxis}),J=j.filter(function(a){return"area"==a.type&&1==a.yAxis}),K=j.filter(function(a){return"area"==a.type&&2==a.yAxis});if(!(j&&j.length&&j.filter(function(a){return a.values.length}).length))return a.utils.noData(b,C),b;C.selectAll(".nv-noData").remove();var L=j.filter(function(a){return!a.disabled&&1==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})}),M=j.filter(function(a){return!a.disabled&&2==a.yAxis}).map(function(a){return a.values.map(function(a){return{x:a.x,y:a.y}})});o.domain(d3.extent(d3.merge(L.concat(M)),function(a){return a.x})).range([0,D]);var N=C.selectAll("g.wrap.multiChart").data([j]),O=N.enter().append("g").attr("class","wrap nvd3 multiChart").append("g");O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y1 nv-axis"),O.append("g").attr("class","nv-y2 nv-axis"),O.append("g").attr("class","lines1Wrap"),O.append("g").attr("class","lines2Wrap"),O.append("g").attr("class","bars1Wrap"),O.append("g").attr("class","bars2Wrap"),O.append("g").attr("class","stack1Wrap"),O.append("g").attr("class","stack2Wrap"),O.append("g").attr("class","legendWrap");var P=N.select("g"),Q=j.map(function(a,b){return j[b].color||f(a,b)});if(i){var R=A.align()?D/2:D,S=A.align()?R:0;A.width(R),A.color(Q),P.select(".legendWrap").datum(j.map(function(a){return a.originalKey=void 0===a.originalKey?a.key:a.originalKey,a.key=a.originalKey+(1==a.yAxis?"":" (right axis)"),a})).call(A),e.top!=A.height()&&(e.top=A.height(),E=a.utils.availableHeight(h,C,e)),P.select(".legendWrap").attr("transform","translate("+S+","+-e.top+")")}r.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"line"==j[b].type})),s.width(D).height(E).interpolate(m).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"line"==j[b].type})),t.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"bar"==j[b].type})),u.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"bar"==j[b].type})),v.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&1==j[b].yAxis&&"area"==j[b].type})),w.width(D).height(E).color(Q.filter(function(a,b){return!j[b].disabled&&2==j[b].yAxis&&"area"==j[b].type})),P.attr("transform","translate("+e.left+","+e.top+")");var T=P.select(".lines1Wrap").datum(F.filter(function(a){return!a.disabled})),U=P.select(".bars1Wrap").datum(H.filter(function(a){return!a.disabled})),V=P.select(".stack1Wrap").datum(J.filter(function(a){return!a.disabled})),W=P.select(".lines2Wrap").datum(G.filter(function(a){return!a.disabled})),X=P.select(".bars2Wrap").datum(I.filter(function(a){return!a.disabled})),Y=P.select(".stack2Wrap").datum(K.filter(function(a){return!a.disabled})),Z=J.length?J.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[],$=K.length?K.map(function(a){return a.values}).reduce(function(a,b){return a.map(function(a,c){return{x:a.x,y:a.y+b[c].y}})}).concat([{x:0,y:0}]):[];p.domain(c||d3.extent(d3.merge(L).concat(Z),function(a){return a.y})).range([0,E]),q.domain(d||d3.extent(d3.merge(M).concat($),function(a){return a.y})).range([0,E]),r.yDomain(p.domain()),t.yDomain(p.domain()),v.yDomain(p.domain()),s.yDomain(q.domain()),u.yDomain(q.domain()),w.yDomain(q.domain()),J.length&&d3.transition(V).call(v),K.length&&d3.transition(Y).call(w),H.length&&d3.transition(U).call(t),I.length&&d3.transition(X).call(u),F.length&&d3.transition(T).call(r),G.length&&d3.transition(W).call(s),x._ticks(a.utils.calcTicksX(D/100,j)).tickSize(-E,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+E+")"),d3.transition(P.select(".nv-x.nv-axis")).call(x),y._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y1.nv-axis")).call(y),z._ticks(a.utils.calcTicksY(E/36,j)).tickSize(-D,0),d3.transition(P.select(".nv-y2.nv-axis")).call(z),P.select(".nv-y1.nv-axis").classed("nv-disabled",L.length?!1:!0).attr("transform","translate("+o.range()[0]+",0)"),P.select(".nv-y2.nv-axis").classed("nv-disabled",M.length?!1:!0).attr("transform","translate("+o.range()[1]+",0)"),A.dispatch.on("stateChange",function(){b.update()}),r.dispatch.on("elementMouseover.tooltip",k),s.dispatch.on("elementMouseover.tooltip",k),r.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),s.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),v.dispatch.on("elementMouseover.tooltip",l),w.dispatch.on("elementMouseover.tooltip",l),v.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),w.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMouseover.tooltip",n),u.dispatch.on("elementMouseover.tooltip",n),t.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),u.dispatch.on("elementMouseout.tooltip",function(){B.hidden(!0)}),t.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()}),u.dispatch.on("elementMousemove.tooltip",function(){B.position({top:d3.event.pageY,left:d3.event.pageX})()})}),b}var c,d,e={top:30,right:20,bottom:50,left:60},f=a.utils.defaultColor(),g=null,h=null,i=!0,j=null,k=function(a){return a.x},l=function(a){return a.y},m="monotone",n=!0,o=d3.scale.linear(),p=d3.scale.linear(),q=d3.scale.linear(),r=a.models.line().yScale(p),s=a.models.line().yScale(q),t=a.models.multiBar().stacked(!1).yScale(p),u=a.models.multiBar().stacked(!1).yScale(q),v=a.models.stackedArea().yScale(p),w=a.models.stackedArea().yScale(q),x=a.models.axis().scale(o).orient("bottom").tickPadding(5),y=a.models.axis().scale(p).orient("left"),z=a.models.axis().scale(q).orient("right"),A=a.models.legend().height(30),B=a.models.tooltip(),C=d3.dispatch();return b.dispatch=C,b.lines1=r,b.lines2=s,b.bars1=t,b.bars2=u,b.stack1=v,b.stack2=w,b.xAxis=x,b.yAxis1=y,b.yAxis2=z,b.tooltip=B,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},showLegend:{get:function(){return i},set:function(a){i=a}},yDomain1:{get:function(){return c},set:function(a){c=a}},yDomain2:{get:function(){return d},set:function(a){d=a}},noData:{get:function(){return j},set:function(a){j=a}},interpolate:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return B.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),B.enabled(!!b)}},tooltipContent:{get:function(){return B.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),B.contentGenerator(b)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return f},set:function(b){f=a.utils.getColor(b)}},x:{get:function(){return k},set:function(a){k=a,r.x(a),s.x(a),t.x(a),u.x(a),v.x(a),w.x(a)}},y:{get:function(){return l},set:function(a){l=a,r.y(a),s.y(a),v.y(a),w.y(a),t.y(a),u.y(a)}},useVoronoi:{get:function(){return n},set:function(a){n=a,r.useVoronoi(a),s.useVoronoi(a),v.useVoronoi(a),w.useVoronoi(a)}}}),a.utils.initOptions(b),b},a.models.ohlcBar=function(){"use strict";function b(y){return y.each(function(b){k=d3.select(this);var y=a.utils.availableWidth(h,k,g),A=a.utils.availableHeight(i,k,g);a.utils.initSVG(k);var B=y/b[0].values.length*.9;l.domain(c||d3.extent(b[0].values.map(n).concat(t))),l.range(v?e||[.5*y/b[0].values.length,y*(b[0].values.length-.5)/b[0].values.length]:e||[5+B/2,y-B/2-5]),m.domain(d||[d3.min(b[0].values.map(s).concat(u)),d3.max(b[0].values.map(r).concat(u))]).range(f||[A,0]),l.domain()[0]===l.domain()[1]&&l.domain(l.domain()[0]?[l.domain()[0]-.01*l.domain()[0],l.domain()[1]+.01*l.domain()[1]]:[-1,1]),m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]+.01*m.domain()[0],m.domain()[1]-.01*m.domain()[1]]:[-1,1]);var C=d3.select(this).selectAll("g.nv-wrap.nv-ohlcBar").data([b[0].values]),D=C.enter().append("g").attr("class","nvd3 nv-wrap nv-ohlcBar"),E=D.append("defs"),F=D.append("g"),G=C.select("g");F.append("g").attr("class","nv-ticks"),C.attr("transform","translate("+g.left+","+g.top+")"),k.on("click",function(a,b){z.chartClick({data:a,index:b,pos:d3.event,id:j})}),E.append("clipPath").attr("id","nv-chart-clip-path-"+j).append("rect"),C.select("#nv-chart-clip-path-"+j+" rect").attr("width",y).attr("height",A),G.attr("clip-path",w?"url(#nv-chart-clip-path-"+j+")":"");var H=C.select(".nv-ticks").selectAll(".nv-tick").data(function(a){return a});H.exit().remove(),H.enter().append("path").attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}).attr("d",function(a,b){return"m0,0l0,"+(m(p(a,b))-m(r(a,b)))+"l"+-B/2+",0l"+B/2+",0l0,"+(m(s(a,b))-m(p(a,b)))+"l0,"+(m(q(a,b))-m(s(a,b)))+"l"+B/2+",0l"+-B/2+",0z"}).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("fill",function(){return x[0]}).attr("stroke",function(){return x[0]}).attr("x",0).attr("y",function(a,b){return m(Math.max(0,o(a,b)))}).attr("height",function(a,b){return Math.abs(m(o(a,b))-m(0))}),H.attr("class",function(a,b,c){return(p(a,b)>q(a,b)?"nv-tick negative":"nv-tick positive")+" nv-tick-"+c+"-"+b}),d3.transition(H).attr("transform",function(a,b){return"translate("+l(n(a,b))+","+m(r(a,b))+")"}).attr("d",function(a,c){var d=y/b[0].values.length*.9;return"m0,0l0,"+(m(p(a,c))-m(r(a,c)))+"l"+-d/2+",0l"+d/2+",0l0,"+(m(s(a,c))-m(p(a,c)))+"l0,"+(m(q(a,c))-m(s(a,c)))+"l"+d/2+",0l"+-d/2+",0z"})}),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=Math.floor(1e4*Math.random()),k=null,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=function(a){return a.open},q=function(a){return a.close},r=function(a){return a.high},s=function(a){return a.low},t=[],u=[],v=!1,w=!0,x=a.utils.defaultColor(),y=!1,z=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd","chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove");return b.highlightPoint=function(a,c){b.clearHighlights(),k.select(".nv-ohlcBar .nv-tick-0-"+a).classed("hover",c)},b.clearHighlights=function(){k.select(".nv-ohlcBar .nv-tick.hover").classed("hover",!1)},b.dispatch=z,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},padData:{get:function(){return v},set:function(a){v=a}},clipEdge:{get:function(){return w},set:function(a){w=a}},id:{get:function(){return j},set:function(a){j=a}},interactive:{get:function(){return y},set:function(a){y=a}},x:{get:function(){return n},set:function(a){n=a}},y:{get:function(){return o},set:function(a){o=a}},open:{get:function(){return p()},set:function(a){p=a}},close:{get:function(){return q()},set:function(a){q=a}},high:{get:function(){return r},set:function(a){r=a}},low:{get:function(){return s},set:function(a){s=a}},margin:{get:function(){return g},set:function(a){g.top=void 0!=a.top?a.top:g.top,g.right=void 0!=a.right?a.right:g.right,g.bottom=void 0!=a.bottom?a.bottom:g.bottom,g.left=void 0!=a.left?a.left:g.left -}},color:{get:function(){return x},set:function(b){x=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.parallelCoordinates=function(){"use strict";function b(p){return p.each(function(b){function p(a){return F(h.map(function(b){if(isNaN(a[b])||isNaN(parseFloat(a[b]))){var c=g[b].domain(),d=g[b].range(),e=c[0]-(c[1]-c[0])/9;if(J.indexOf(b)<0){var h=d3.scale.linear().domain([e,c[1]]).range([x-12,d[1]]);g[b].brush.y(h),J.push(b)}return[f(b),g[b](e)]}return J.length>0?(D.style("display","inline"),E.style("display","inline")):(D.style("display","none"),E.style("display","none")),[f(b),g[b](a[b])]}))}function q(){var a=h.filter(function(a){return!g[a].brush.empty()}),b=a.map(function(a){return g[a].brush.extent()});k=[],a.forEach(function(a,c){k[c]={dimension:a,extent:b[c]}}),l=[],M.style("display",function(c){var d=a.every(function(a,d){return isNaN(c[a])&&b[d][0]==g[a].brush.y().domain()[0]?!0:b[d][0]<=c[a]&&c[a]<=b[d][1]});return d&&l.push(c),d?null:"none"}),o.brush({filters:k,active:l})}function r(a){m[a]=this.parentNode.__origin__=f(a),L.attr("visibility","hidden")}function s(a){m[a]=Math.min(w,Math.max(0,this.parentNode.__origin__+=d3.event.x)),M.attr("d",p),h.sort(function(a,b){return u(a)-u(b)}),f.domain(h),N.attr("transform",function(a){return"translate("+u(a)+")"})}function t(a){delete this.parentNode.__origin__,delete m[a],d3.select(this.parentNode).attr("transform","translate("+f(a)+")"),M.attr("d",p),L.attr("d",p).attr("visibility",null)}function u(a){var b=m[a];return null==b?f(a):b}var v=d3.select(this),w=a.utils.availableWidth(d,v,c),x=a.utils.availableHeight(e,v,c);a.utils.initSVG(v),l=b,f.rangePoints([0,w],1).domain(h);var y={};h.forEach(function(a){var c=d3.extent(b,function(b){return+b[a]});return y[a]=!1,void 0===c[0]&&(y[a]=!0,c[0]=0,c[1]=0),c[0]===c[1]&&(c[0]=c[0]-1,c[1]=c[1]+1),g[a]=d3.scale.linear().domain(c).range([.9*(x-12),0]),g[a].brush=d3.svg.brush().y(g[a]).on("brush",q),"name"!=a});var z=v.selectAll("g.nv-wrap.nv-parallelCoordinates").data([b]),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-parallelCoordinates"),B=A.append("g"),C=z.select("g");B.append("g").attr("class","nv-parallelCoordinates background"),B.append("g").attr("class","nv-parallelCoordinates foreground"),B.append("g").attr("class","nv-parallelCoordinates missingValuesline"),z.attr("transform","translate("+c.left+","+c.top+")");var D,E,F=d3.svg.line().interpolate("cardinal").tension(n),G=d3.svg.axis().orient("left"),H=d3.behavior.drag().on("dragstart",r).on("drag",s).on("dragend",t),I=f.range()[1]-f.range()[0],J=[],K=[0+I/2,x-12,w-I/2,x-12];D=z.select(".missingValuesline").selectAll("line").data([K]),D.enter().append("line"),D.exit().remove(),D.attr("x1",function(a){return a[0]}).attr("y1",function(a){return a[1]}).attr("x2",function(a){return a[2]}).attr("y2",function(a){return a[3]}),E=z.select(".missingValuesline").selectAll("text").data(["undefined values"]),E.append("text").data(["undefined values"]),E.enter().append("text"),E.exit().remove(),E.attr("y",x).attr("x",w-92-I/2).text(function(a){return a});var L=z.select(".background").selectAll("path").data(b);L.enter().append("path"),L.exit().remove(),L.attr("d",p);var M=z.select(".foreground").selectAll("path").data(b);M.enter().append("path"),M.exit().remove(),M.attr("d",p).attr("stroke",j),M.on("mouseover",function(a,b){d3.select(this).classed("hover",!0),o.elementMouseover({label:a.name,data:a.data,index:b,pos:[d3.mouse(this.parentNode)[0],d3.mouse(this.parentNode)[1]]})}),M.on("mouseout",function(a,b){d3.select(this).classed("hover",!1),o.elementMouseout({label:a.name,data:a.data,index:b})});var N=C.selectAll(".dimension").data(h),O=N.enter().append("g").attr("class","nv-parallelCoordinates dimension");O.append("g").attr("class","nv-parallelCoordinates nv-axis"),O.append("g").attr("class","nv-parallelCoordinates-brush"),O.append("text").attr("class","nv-parallelCoordinates nv-label"),N.attr("transform",function(a){return"translate("+f(a)+",0)"}),N.exit().remove(),N.select(".nv-label").style("cursor","move").attr("dy","-1em").attr("text-anchor","middle").text(String).on("mouseover",function(a){o.elementMouseover({dim:a,pos:[d3.mouse(this.parentNode.parentNode)[0],d3.mouse(this.parentNode.parentNode)[1]]})}).on("mouseout",function(a){o.elementMouseout({dim:a})}).call(H),N.select(".nv-axis").each(function(a,b){d3.select(this).call(G.scale(g[a]).tickFormat(d3.format(i[b])))}),N.select(".nv-parallelCoordinates-brush").each(function(a){d3.select(this).call(g[a].brush)}).selectAll("rect").attr("x",-8).attr("width",16)}),b}var c={top:30,right:0,bottom:10,left:0},d=null,e=null,f=d3.scale.ordinal(),g={},h=[],i=[],j=a.utils.defaultColor(),k=[],l=[],m=[],n=1,o=d3.dispatch("brush","elementMouseover","elementMouseout");return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},dimensionNames:{get:function(){return h},set:function(a){h=a}},dimensionFormats:{get:function(){return i},set:function(a){i=a}},lineTension:{get:function(){return n},set:function(a){n=a}},dimensions:{get:function(){return h},set:function(b){a.deprecated("dimensions","use dimensionNames instead"),h=b}},margin:{get:function(){return c},set:function(a){c.top=void 0!==a.top?a.top:c.top,c.right=void 0!==a.right?a.right:c.right,c.bottom=void 0!==a.bottom?a.bottom:c.bottom,c.left=void 0!==a.left?a.left:c.left}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.pie=function(){"use strict";function b(E){return D.reset(),E.each(function(b){function E(a,b){a.endAngle=isNaN(a.endAngle)?0:a.endAngle,a.startAngle=isNaN(a.startAngle)?0:a.startAngle,p||(a.innerRadius=0);var c=d3.interpolate(this._current,a);return this._current=c(0),function(a){return B[b](c(a))}}var F=d-c.left-c.right,G=e-c.top-c.bottom,H=Math.min(F,G)/2,I=[],J=[];if(i=d3.select(this),0===z.length)for(var K=H-H/5,L=y*H,M=0;Mc)return"";if("function"==typeof n)d=n(a,b,{key:f(a.data),value:g(a.data),percent:k(c)});else switch(n){case"key":d=f(a.data);break;case"value":d=k(g(a.data));break;case"percent":d=d3.format("%")(c)}return d})}}),D.renderEnd("pie immediate"),b}var c={top:0,right:0,bottom:0,left:0},d=500,e=500,f=function(a){return a.x},g=function(a){return a.y},h=Math.floor(1e4*Math.random()),i=null,j=a.utils.defaultColor(),k=d3.format(",.2f"),l=!0,m=!1,n="key",o=.02,p=!1,q=!1,r=!0,s=0,t=!1,u=!1,v=!1,w=!1,x=0,y=.5,z=[],A=d3.dispatch("chartClick","elementClick","elementDblClick","elementMouseover","elementMouseout","elementMousemove","renderEnd"),B=[],C=[],D=a.utils.renderWatch(A);return b.dispatch=A,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{arcsRadius:{get:function(){return z},set:function(a){z=a}},width:{get:function(){return d},set:function(a){d=a}},height:{get:function(){return e},set:function(a){e=a}},showLabels:{get:function(){return l},set:function(a){l=a}},title:{get:function(){return q},set:function(a){q=a}},titleOffset:{get:function(){return s},set:function(a){s=a}},labelThreshold:{get:function(){return o},set:function(a){o=a}},valueFormat:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return f},set:function(a){f=a}},id:{get:function(){return h},set:function(a){h=a}},endAngle:{get:function(){return w},set:function(a){w=a}},startAngle:{get:function(){return u},set:function(a){u=a}},padAngle:{get:function(){return v},set:function(a){v=a}},cornerRadius:{get:function(){return x},set:function(a){x=a}},donutRatio:{get:function(){return y},set:function(a){y=a}},labelsOutside:{get:function(){return m},set:function(a){m=a}},labelSunbeamLayout:{get:function(){return t},set:function(a){t=a}},donut:{get:function(){return p},set:function(a){p=a}},growOnHover:{get:function(){return r},set:function(a){r=a}},pieLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("pieLabelsOutside","use labelsOutside instead")}},donutLabelsOutside:{get:function(){return m},set:function(b){m=b,a.deprecated("donutLabelsOutside","use labelsOutside instead")}},labelFormat:{get:function(){return k},set:function(b){k=b,a.deprecated("labelFormat","use valueFormat instead")}},margin:{get:function(){return c},set:function(a){c.top="undefined"!=typeof a.top?a.top:c.top,c.right="undefined"!=typeof a.right?a.right:c.right,c.bottom="undefined"!=typeof a.bottom?a.bottom:c.bottom,c.left="undefined"!=typeof a.left?a.left:c.left}},y:{get:function(){return g},set:function(a){g=d3.functor(a)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},labelType:{get:function(){return n},set:function(a){n=a||"key"}}}),a.utils.initOptions(b),b},a.models.pieChart=function(){"use strict";function b(e){return q.reset(),q.models(c),e.each(function(e){var k=d3.select(this);a.utils.initSVG(k);var n=a.utils.availableWidth(g,k,f),o=a.utils.availableHeight(h,k,f);if(b.update=function(){k.transition().call(b)},b.container=this,l.setter(s(e),b.update).getter(r(e)).update(),l.disabled=e.map(function(a){return!!a.disabled}),!m){var q;m={};for(q in l)m[q]=l[q]instanceof Array?l[q].slice(0):l[q]}if(!e||!e.length)return a.utils.noData(b,k),b;k.selectAll(".nv-noData").remove();var t=k.selectAll("g.nv-wrap.nv-pieChart").data([e]),u=t.enter().append("g").attr("class","nvd3 nv-wrap nv-pieChart").append("g"),v=t.select("g");if(u.append("g").attr("class","nv-pieWrap"),u.append("g").attr("class","nv-legendWrap"),i)if("top"===j)d.width(n).key(c.x()),t.select(".nv-legendWrap").datum(e).call(d),f.top!=d.height()&&(f.top=d.height(),o=a.utils.availableHeight(h,k,f)),t.select(".nv-legendWrap").attr("transform","translate(0,"+-f.top+")");else if("right"===j){var w=a.models.legend().width();w>n/2&&(w=n/2),d.height(o).key(c.x()),d.width(w),n-=d.width(),t.select(".nv-legendWrap").datum(e).call(d).attr("transform","translate("+n+",0)")}t.attr("transform","translate("+f.left+","+f.top+")"),c.width(n).height(o);var x=v.select(".nv-pieWrap").datum([e]);d3.transition(x).call(c),d.dispatch.on("stateChange",function(a){for(var c in a)l[c]=a[c];p.stateChange(l),b.update()}),p.on("changeState",function(a){"undefined"!=typeof a.disabled&&(e.forEach(function(b,c){b.disabled=a.disabled[c]}),l.disabled=a.disabled),b.update()})}),q.renderEnd("pieChart immediate"),b}var c=a.models.pie(),d=a.models.legend(),e=a.models.tooltip(),f={top:30,right:20,bottom:20,left:20},g=null,h=null,i=!0,j="top",k=a.utils.defaultColor(),l=a.utils.state(),m=null,n=null,o=250,p=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd");e.headerEnabled(!1).duration(0).valueFormatter(function(a,b){return c.valueFormat()(a,b)});var q=a.utils.renderWatch(p),r=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},s=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:b.x()(a.data),value:b.y()(a.data),color:a.color},e.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){e.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){e.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.legend=d,b.dispatch=p,b.pie=c,b.tooltip=e,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return i},set:function(a){i=a}},legendPosition:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return m},set:function(a){m=a}},tooltips:{get:function(){return e.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),e.enabled(!!b)}},tooltipContent:{get:function(){return e.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),e.contentGenerator(b)}},color:{get:function(){return k},set:function(a){k=a,d.color(k),c.color(k)}},duration:{get:function(){return o},set:function(a){o=a,q.reset(o)}},margin:{get:function(){return f},set:function(a){f.top=void 0!==a.top?a.top:f.top,f.right=void 0!==a.right?a.right:f.right,f.bottom=void 0!==a.bottom?a.bottom:f.bottom,f.left=void 0!==a.left?a.left:f.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.scatter=function(){"use strict";function b(N){return P.reset(),N.each(function(b){function N(){if(O=!1,!w)return!1;if(M===!0){var a=d3.merge(b.map(function(a,b){return a.values.map(function(a,c){var d=p(a,c),e=q(a,c);return[m(d)+1e-4*Math.random(),n(e)+1e-4*Math.random(),b,c,a]}).filter(function(a,b){return x(a[4],b)})}));if(0==a.length)return!1;a.length<3&&(a.push([m.range()[0]-20,n.range()[0]-20,null,null]),a.push([m.range()[1]+20,n.range()[1]+20,null,null]),a.push([m.range()[0]-20,n.range()[0]+20,null,null]),a.push([m.range()[1]+20,n.range()[1]-20,null,null]));var c=d3.geom.polygon([[-10,-10],[-10,i+10],[h+10,i+10],[h+10,-10]]),d=d3.geom.voronoi(a).map(function(b,d){return{data:c.clip(b),series:a[d][2],point:a[d][3]}});U.select(".nv-point-paths").selectAll("path").remove();var e=U.select(".nv-point-paths").selectAll("path").data(d),f=e.enter().append("svg:path").attr("d",function(a){return a&&a.data&&0!==a.data.length?"M"+a.data.join(",")+"Z":"M 0 0"}).attr("id",function(a,b){return"nv-path-"+b}).attr("clip-path",function(a,b){return"url(#nv-clip-"+b+")"});C&&f.style("fill",d3.rgb(230,230,230)).style("fill-opacity",.4).style("stroke-opacity",1).style("stroke",d3.rgb(200,200,200)),B&&(U.select(".nv-point-clips").selectAll("clipPath").remove(),U.select(".nv-point-clips").selectAll("clipPath").data(a).enter().append("svg:clipPath").attr("id",function(a,b){return"nv-clip-"+b}).append("svg:circle").attr("cx",function(a){return a[0]}).attr("cy",function(a){return a[1]}).attr("r",D));var k=function(a,c){if(O)return 0;var d=b[a.series];if(void 0!==d){var e=d.values[a.point];e.color=j(d,a.series),e.x=p(e),e.y=q(e);var f=l.node().getBoundingClientRect(),h=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,k={left:m(p(e,a.point))+f.left+i+g.left+10,top:n(q(e,a.point))+f.top+h+g.top+10};c({point:e,series:d,pos:k,seriesIndex:a.series,pointIndex:a.point})}};e.on("click",function(a){k(a,L.elementClick)}).on("dblclick",function(a){k(a,L.elementDblClick)}).on("mouseover",function(a){k(a,L.elementMouseover)}).on("mouseout",function(a){k(a,L.elementMouseout)})}else U.select(".nv-groups").selectAll(".nv-group").selectAll(".nv-point").on("click",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("dblclick",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementDblClick({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c})}).on("mouseover",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseover({point:e,series:d,pos:[m(p(e,c))+g.left,n(q(e,c))+g.top],seriesIndex:a.series,pointIndex:c,color:j(a,c)})}).on("mouseout",function(a,c){if(O||!b[a.series])return 0;var d=b[a.series],e=d.values[c];L.elementMouseout({point:e,series:d,seriesIndex:a.series,pointIndex:c,color:j(a,c)})})}l=d3.select(this);var R=a.utils.availableWidth(h,l,g),S=a.utils.availableHeight(i,l,g);a.utils.initSVG(l),b.forEach(function(a,b){a.values.forEach(function(a){a.series=b})});var T=E&&F&&I?[]:d3.merge(b.map(function(a){return a.values.map(function(a,b){return{x:p(a,b),y:q(a,b),size:r(a,b)}})}));m.domain(E||d3.extent(T.map(function(a){return a.x}).concat(t))),m.range(y&&b[0]?G||[(R*z+R)/(2*b[0].values.length),R-R*(1+z)/(2*b[0].values.length)]:G||[0,R]),n.domain(F||d3.extent(T.map(function(a){return a.y}).concat(u))).range(H||[S,0]),o.domain(I||d3.extent(T.map(function(a){return a.size}).concat(v))).range(J||Q),K=m.domain()[0]===m.domain()[1]||n.domain()[0]===n.domain()[1],m.domain()[0]===m.domain()[1]&&m.domain(m.domain()[0]?[m.domain()[0]-.01*m.domain()[0],m.domain()[1]+.01*m.domain()[1]]:[-1,1]),n.domain()[0]===n.domain()[1]&&n.domain(n.domain()[0]?[n.domain()[0]-.01*n.domain()[0],n.domain()[1]+.01*n.domain()[1]]:[-1,1]),isNaN(m.domain()[0])&&m.domain([-1,1]),isNaN(n.domain()[0])&&n.domain([-1,1]),c=c||m,d=d||n,e=e||o;var U=l.selectAll("g.nv-wrap.nv-scatter").data([b]),V=U.enter().append("g").attr("class","nvd3 nv-wrap nv-scatter nv-chart-"+k),W=V.append("defs"),X=V.append("g"),Y=U.select("g");U.classed("nv-single-point",K),X.append("g").attr("class","nv-groups"),X.append("g").attr("class","nv-point-paths"),V.append("g").attr("class","nv-point-clips"),U.attr("transform","translate("+g.left+","+g.top+")"),W.append("clipPath").attr("id","nv-edge-clip-"+k).append("rect"),U.select("#nv-edge-clip-"+k+" rect").attr("width",R).attr("height",S>0?S:0),Y.attr("clip-path",A?"url(#nv-edge-clip-"+k+")":""),O=!0;var Z=U.select(".nv-groups").selectAll(".nv-group").data(function(a){return a},function(a){return a.key});Z.enter().append("g").style("stroke-opacity",1e-6).style("fill-opacity",1e-6),Z.exit().remove(),Z.attr("class",function(a,b){return"nv-group nv-series-"+b}).classed("hover",function(a){return a.hover}),Z.watchTransition(P,"scatter: groups").style("fill",function(a,b){return j(a,b)}).style("stroke",function(a,b){return j(a,b)}).style("stroke-opacity",1).style("fill-opacity",.5);var $=Z.selectAll("path.nv-point").data(function(a){return a.values.map(function(a,b){return[a,b]}).filter(function(a,b){return x(a[0],b)})});$.enter().append("path").style("fill",function(a){return a.color}).style("stroke",function(a){return a.color}).attr("transform",function(a){return"translate("+c(p(a[0],a[1]))+","+d(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),$.exit().remove(),Z.exit().selectAll("path.nv-point").watchTransition(P,"scatter exit").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).remove(),$.each(function(a){d3.select(this).classed("nv-point",!0).classed("nv-point-"+a[1],!0).classed("nv-noninteractive",!w).classed("hover",!1)}),$.watchTransition(P,"scatter points").attr("transform",function(a){return"translate("+m(p(a[0],a[1]))+","+n(q(a[0],a[1]))+")"}).attr("d",a.utils.symbol().type(function(a){return s(a[0])}).size(function(a){return o(r(a[0],a[1]))})),clearTimeout(f),f=setTimeout(N,300),c=m.copy(),d=n.copy(),e=o.copy()}),P.renderEnd("scatter immediate"),b}var c,d,e,f,g={top:0,right:0,bottom:0,left:0},h=null,i=null,j=a.utils.defaultColor(),k=Math.floor(1e5*Math.random()),l=null,m=d3.scale.linear(),n=d3.scale.linear(),o=d3.scale.linear(),p=function(a){return a.x},q=function(a){return a.y},r=function(a){return a.size||1},s=function(a){return a.shape||"circle"},t=[],u=[],v=[],w=!0,x=function(a){return!a.notActive},y=!1,z=.1,A=!1,B=!0,C=!1,D=function(){return 25},E=null,F=null,G=null,H=null,I=null,J=null,K=!1,L=d3.dispatch("elementClick","elementDblClick","elementMouseover","elementMouseout","renderEnd"),M=!0,N=250,O=!1,P=a.utils.renderWatch(L,N),Q=[16,256];return b.dispatch=L,b.options=a.utils.optionsFunc.bind(b),b._calls=new function(){this.clearHighlights=function(){return a.dom.write(function(){l.selectAll(".nv-point.hover").classed("hover",!1)}),null},this.highlightPoint=function(b,c,d){a.dom.write(function(){l.select(" .nv-series-"+b+" .nv-point-"+c).classed("hover",d)})}},L.on("elementMouseover.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!0)}),L.on("elementMouseout.point",function(a){w&&b._calls.highlightPoint(a.seriesIndex,a.pointIndex,!1)}),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xScale:{get:function(){return m},set:function(a){m=a}},yScale:{get:function(){return n},set:function(a){n=a}},pointScale:{get:function(){return o},set:function(a){o=a}},xDomain:{get:function(){return E},set:function(a){E=a}},yDomain:{get:function(){return F},set:function(a){F=a}},pointDomain:{get:function(){return I},set:function(a){I=a}},xRange:{get:function(){return G},set:function(a){G=a}},yRange:{get:function(){return H},set:function(a){H=a}},pointRange:{get:function(){return J},set:function(a){J=a}},forceX:{get:function(){return t},set:function(a){t=a}},forceY:{get:function(){return u},set:function(a){u=a}},forcePoint:{get:function(){return v},set:function(a){v=a}},interactive:{get:function(){return w},set:function(a){w=a}},pointActive:{get:function(){return x},set:function(a){x=a}},padDataOuter:{get:function(){return z},set:function(a){z=a}},padData:{get:function(){return y},set:function(a){y=a}},clipEdge:{get:function(){return A},set:function(a){A=a}},clipVoronoi:{get:function(){return B},set:function(a){B=a}},clipRadius:{get:function(){return D},set:function(a){D=a}},showVoronoi:{get:function(){return C},set:function(a){C=a}},id:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return p},set:function(a){p=d3.functor(a)}},y:{get:function(){return q},set:function(a){q=d3.functor(a)}},pointSize:{get:function(){return r},set:function(a){r=d3.functor(a)}},pointShape:{get:function(){return s},set:function(a){s=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},duration:{get:function(){return N},set:function(a){N=a,P.reset(N)}},color:{get:function(){return j},set:function(b){j=a.utils.getColor(b)}},useVoronoi:{get:function(){return M},set:function(a){M=a,M===!1&&(B=!1)}}}),a.utils.initOptions(b),b},a.models.scatterChart=function(){"use strict";function b(z){return D.reset(),D.models(c),t&&D.models(d),u&&D.models(e),q&&D.models(g),r&&D.models(h),z.each(function(z){m=d3.select(this),a.utils.initSVG(m);var G=a.utils.availableWidth(k,m,j),H=a.utils.availableHeight(l,m,j);if(b.update=function(){0===A?m.call(b):m.transition().duration(A).call(b)},b.container=this,w.setter(F(z),b.update).getter(E(z)).update(),w.disabled=z.map(function(a){return!!a.disabled}),!x){var I;x={};for(I in w)x[I]=w[I]instanceof Array?w[I].slice(0):w[I]}if(!(z&&z.length&&z.filter(function(a){return a.values.length}).length))return a.utils.noData(b,m),D.renderEnd("scatter immediate"),b;m.selectAll(".nv-noData").remove(),o=c.xScale(),p=c.yScale();var J=m.selectAll("g.nv-wrap.nv-scatterChart").data([z]),K=J.enter().append("g").attr("class","nvd3 nv-wrap nv-scatterChart nv-chart-"+c.id()),L=K.append("g"),M=J.select("g");if(L.append("rect").attr("class","nvd3 nv-background").style("pointer-events","none"),L.append("g").attr("class","nv-x nv-axis"),L.append("g").attr("class","nv-y nv-axis"),L.append("g").attr("class","nv-scatterWrap"),L.append("g").attr("class","nv-regressionLinesWrap"),L.append("g").attr("class","nv-distWrap"),L.append("g").attr("class","nv-legendWrap"),v&&M.select(".nv-y.nv-axis").attr("transform","translate("+G+",0)"),s){var N=G;f.width(N),J.select(".nv-legendWrap").datum(z).call(f),j.top!=f.height()&&(j.top=f.height(),H=a.utils.availableHeight(l,m,j)),J.select(".nv-legendWrap").attr("transform","translate(0,"+-j.top+")")}J.attr("transform","translate("+j.left+","+j.top+")"),c.width(G).height(H).color(z.map(function(a,b){return a.color=a.color||n(a,b),a.color}).filter(function(a,b){return!z[b].disabled})),J.select(".nv-scatterWrap").datum(z.filter(function(a){return!a.disabled})).call(c),J.select(".nv-regressionLinesWrap").attr("clip-path","url(#nv-edge-clip-"+c.id()+")");var O=J.select(".nv-regressionLinesWrap").selectAll(".nv-regLines").data(function(a){return a});O.enter().append("g").attr("class","nv-regLines");var P=O.selectAll(".nv-regLine").data(function(a){return[a]});P.enter().append("line").attr("class","nv-regLine").style("stroke-opacity",0),P.filter(function(a){return a.intercept&&a.slope}).watchTransition(D,"scatterPlusLineChart: regline").attr("x1",o.range()[0]).attr("x2",o.range()[1]).attr("y1",function(a){return p(o.domain()[0]*a.slope+a.intercept)}).attr("y2",function(a){return p(o.domain()[1]*a.slope+a.intercept)}).style("stroke",function(a,b,c){return n(a,c)}).style("stroke-opacity",function(a){return a.disabled||"undefined"==typeof a.slope||"undefined"==typeof a.intercept?0:1}),t&&(d.scale(o)._ticks(a.utils.calcTicksX(G/100,z)).tickSize(-H,0),M.select(".nv-x.nv-axis").attr("transform","translate(0,"+p.range()[0]+")").call(d)),u&&(e.scale(p)._ticks(a.utils.calcTicksY(H/36,z)).tickSize(-G,0),M.select(".nv-y.nv-axis").call(e)),q&&(g.getData(c.x()).scale(o).width(G).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionX"),M.select(".nv-distributionX").attr("transform","translate(0,"+p.range()[0]+")").datum(z.filter(function(a){return!a.disabled})).call(g)),r&&(h.getData(c.y()).scale(p).width(H).color(z.map(function(a,b){return a.color||n(a,b)}).filter(function(a,b){return!z[b].disabled})),L.select(".nv-distWrap").append("g").attr("class","nv-distributionY"),M.select(".nv-distributionY").attr("transform","translate("+(v?G:-h.size())+",0)").datum(z.filter(function(a){return!a.disabled})).call(h)),f.dispatch.on("stateChange",function(a){for(var c in a)w[c]=a[c];y.stateChange(w),b.update()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&(z.forEach(function(b,c){b.disabled=a.disabled[c]}),w.disabled=a.disabled),b.update()}),c.dispatch.on("elementMouseout.tooltip",function(a){i.hidden(!0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",0),m.select(".nv-chart-"+c.id()+" .nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",h.size())}),c.dispatch.on("elementMouseover.tooltip",function(a){m.select(".nv-series-"+a.seriesIndex+" .nv-distx-"+a.pointIndex).attr("y1",a.pos.top-H-j.top),m.select(".nv-series-"+a.seriesIndex+" .nv-disty-"+a.pointIndex).attr("x2",a.pos.left+g.size()-j.left),i.position(a.pos).data(a).hidden(!1)}),B=o.copy(),C=p.copy()}),D.renderEnd("scatter with line immediate"),b}var c=a.models.scatter(),d=a.models.axis(),e=a.models.axis(),f=a.models.legend(),g=a.models.distribution(),h=a.models.distribution(),i=a.models.tooltip(),j={top:30,right:20,bottom:50,left:75},k=null,l=null,m=null,n=a.utils.defaultColor(),o=c.xScale(),p=c.yScale(),q=!1,r=!1,s=!0,t=!0,u=!0,v=!1,w=a.utils.state(),x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=null,A=250;c.xScale(o).yScale(p),d.orient("bottom").tickPadding(10),e.orient(v?"right":"left").tickPadding(10),g.axis("x"),h.axis("y"),i.headerFormatter(function(a,b){return d.tickFormat()(a,b)}).valueFormatter(function(a,b){return e.tickFormat()(a,b)});var B,C,D=a.utils.renderWatch(y,A),E=function(a){return function(){return{active:a.map(function(a){return!a.disabled})}}},F=function(a){return function(b){void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}};return b.dispatch=y,b.scatter=c,b.legend=f,b.xAxis=d,b.yAxis=e,b.distX=g,b.distY=h,b.tooltip=i,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return k},set:function(a){k=a}},height:{get:function(){return l},set:function(a){l=a}},container:{get:function(){return m},set:function(a){m=a}},showDistX:{get:function(){return q},set:function(a){q=a}},showDistY:{get:function(){return r},set:function(a){r=a}},showLegend:{get:function(){return s},set:function(a){s=a}},showXAxis:{get:function(){return t},set:function(a){t=a}},showYAxis:{get:function(){return u},set:function(a){u=a}},defaultState:{get:function(){return x},set:function(a){x=a}},noData:{get:function(){return z},set:function(a){z=a}},duration:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return i.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),i.enabled(!!b) -}},tooltipContent:{get:function(){return i.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),i.contentGenerator(b)}},tooltipXContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},tooltipYContent:{get:function(){return i.contentGenerator()},set:function(){a.deprecated("tooltipContent","This option is removed, put values into main tooltip.")}},margin:{get:function(){return j},set:function(a){j.top=void 0!==a.top?a.top:j.top,j.right=void 0!==a.right?a.right:j.right,j.bottom=void 0!==a.bottom?a.bottom:j.bottom,j.left=void 0!==a.left?a.left:j.left}},rightAlignYAxis:{get:function(){return v},set:function(a){v=a,e.orient(a?"right":"left")}},color:{get:function(){return n},set:function(b){n=a.utils.getColor(b),f.color(n),g.color(n),h.color(n)}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.models.sparkline=function(){"use strict";function b(k){return k.each(function(b){var k=h-g.left-g.right,q=i-g.top-g.bottom;j=d3.select(this),a.utils.initSVG(j),l.domain(c||d3.extent(b,n)).range(e||[0,k]),m.domain(d||d3.extent(b,o)).range(f||[q,0]);{var r=j.selectAll("g.nv-wrap.nv-sparkline").data([b]),s=r.enter().append("g").attr("class","nvd3 nv-wrap nv-sparkline");s.append("g"),r.select("g")}r.attr("transform","translate("+g.left+","+g.top+")");var t=r.selectAll("path").data(function(a){return[a]});t.enter().append("path"),t.exit().remove(),t.style("stroke",function(a,b){return a.color||p(a,b)}).attr("d",d3.svg.line().x(function(a,b){return l(n(a,b))}).y(function(a,b){return m(o(a,b))}));var u=r.selectAll("circle.nv-point").data(function(a){function b(b){if(-1!=b){var c=a[b];return c.pointIndex=b,c}return null}var c=a.map(function(a,b){return o(a,b)}),d=b(c.lastIndexOf(m.domain()[1])),e=b(c.indexOf(m.domain()[0])),f=b(c.length-1);return[e,d,f].filter(function(a){return null!=a})});u.enter().append("circle"),u.exit().remove(),u.attr("cx",function(a){return l(n(a,a.pointIndex))}).attr("cy",function(a){return m(o(a,a.pointIndex))}).attr("r",2).attr("class",function(a){return n(a,a.pointIndex)==l.domain()[1]?"nv-point nv-currentValue":o(a,a.pointIndex)==m.domain()[0]?"nv-point nv-minValue":"nv-point nv-maxValue"})}),b}var c,d,e,f,g={top:2,right:0,bottom:2,left:0},h=400,i=32,j=null,k=!0,l=d3.scale.linear(),m=d3.scale.linear(),n=function(a){return a.x},o=function(a){return a.y},p=a.utils.getColor(["#000"]);return b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return h},set:function(a){h=a}},height:{get:function(){return i},set:function(a){i=a}},xDomain:{get:function(){return c},set:function(a){c=a}},yDomain:{get:function(){return d},set:function(a){d=a}},xRange:{get:function(){return e},set:function(a){e=a}},yRange:{get:function(){return f},set:function(a){f=a}},xScale:{get:function(){return l},set:function(a){l=a}},yScale:{get:function(){return m},set:function(a){m=a}},animate:{get:function(){return k},set:function(a){k=a}},x:{get:function(){return n},set:function(a){n=d3.functor(a)}},y:{get:function(){return o},set:function(a){o=d3.functor(a)}},margin:{get:function(){return g},set:function(a){g.top=void 0!==a.top?a.top:g.top,g.right=void 0!==a.right?a.right:g.right,g.bottom=void 0!==a.bottom?a.bottom:g.bottom,g.left=void 0!==a.left?a.left:g.left}},color:{get:function(){return p},set:function(b){p=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sparklinePlus=function(){"use strict";function b(p){return p.each(function(p){function q(){if(!j){var a=z.selectAll(".nv-hoverValue").data(i),b=a.enter().append("g").attr("class","nv-hoverValue").style("stroke-opacity",0).style("fill-opacity",0);a.exit().transition().duration(250).style("stroke-opacity",0).style("fill-opacity",0).remove(),a.attr("transform",function(a){return"translate("+c(e.x()(p[a],a))+",0)"}).transition().duration(250).style("stroke-opacity",1).style("fill-opacity",1),i.length&&(b.append("line").attr("x1",0).attr("y1",-f.top).attr("x2",0).attr("y2",u),b.append("text").attr("class","nv-xValue").attr("x",-6).attr("y",-f.top).attr("text-anchor","end").attr("dy",".9em"),z.select(".nv-hoverValue .nv-xValue").text(k(e.x()(p[i[0]],i[0]))),b.append("text").attr("class","nv-yValue").attr("x",6).attr("y",-f.top).attr("text-anchor","start").attr("dy",".9em"),z.select(".nv-hoverValue .nv-yValue").text(l(e.y()(p[i[0]],i[0]))))}}function r(){function a(a,b){for(var c=Math.abs(e.x()(a[0],0)-b),d=0,f=0;fc;++c){for(b=0,d=0;bb;b++)a[b][c][1]/=d;else for(b=0;e>b;b++)a[b][c][1]=0}for(c=0;f>c;++c)g[c]=0;return g}}),u.renderEnd("stackedArea immediate"),b}var c,d,e={top:0,right:0,bottom:0,left:0},f=960,g=500,h=a.utils.defaultColor(),i=Math.floor(1e5*Math.random()),j=null,k=function(a){return a.x},l=function(a){return a.y},m="stack",n="zero",o="default",p="linear",q=!1,r=a.models.scatter(),s=250,t=d3.dispatch("areaClick","areaMouseover","areaMouseout","renderEnd","elementClick","elementMouseover","elementMouseout");r.pointSize(2.2).pointDomain([2.2,2.2]);var u=a.utils.renderWatch(t,s);return b.dispatch=t,b.scatter=r,r.dispatch.on("elementClick",function(){t.elementClick.apply(this,arguments)}),r.dispatch.on("elementMouseover",function(){t.elementMouseover.apply(this,arguments)}),r.dispatch.on("elementMouseout",function(){t.elementMouseout.apply(this,arguments)}),b.interpolate=function(a){return arguments.length?(p=a,b):p},b.duration=function(a){return arguments.length?(s=a,u.reset(s),r.duration(s),b):s},b.dispatch=t,b.scatter=r,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return f},set:function(a){f=a}},height:{get:function(){return g},set:function(a){g=a}},clipEdge:{get:function(){return q},set:function(a){q=a}},offset:{get:function(){return n},set:function(a){n=a}},order:{get:function(){return o},set:function(a){o=a}},interpolate:{get:function(){return p},set:function(a){p=a}},x:{get:function(){return k},set:function(a){k=d3.functor(a)}},y:{get:function(){return l},set:function(a){l=d3.functor(a)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}},color:{get:function(){return h},set:function(b){h=a.utils.getColor(b)}},style:{get:function(){return m},set:function(a){switch(m=a){case"stack":b.offset("zero"),b.order("default");break;case"stream":b.offset("wiggle"),b.order("inside-out");break;case"stream-center":b.offset("silhouette"),b.order("inside-out");break;case"expand":b.offset("expand"),b.order("default");break;case"stack_percent":b.offset(b.d3_stackedOffset_stackPercent),b.order("default")}}},duration:{get:function(){return s},set:function(a){s=a,u.reset(s),r.duration(s)}}}),a.utils.inheritOptions(b,r),a.utils.initOptions(b),b},a.models.stackedAreaChart=function(){"use strict";function b(k){return F.reset(),F.models(e),r&&F.models(f),s&&F.models(g),k.each(function(k){var x=d3.select(this),F=this;a.utils.initSVG(x);var K=a.utils.availableWidth(m,x,l),L=a.utils.availableHeight(n,x,l);if(b.update=function(){x.transition().duration(C).call(b)},b.container=this,v.setter(I(k),b.update).getter(H(k)).update(),v.disabled=k.map(function(a){return!!a.disabled}),!w){var M;w={};for(M in v)w[M]=v[M]instanceof Array?v[M].slice(0):v[M]}if(!(k&&k.length&&k.filter(function(a){return a.values.length}).length))return a.utils.noData(b,x),b;x.selectAll(".nv-noData").remove(),c=e.xScale(),d=e.yScale();var N=x.selectAll("g.nv-wrap.nv-stackedAreaChart").data([k]),O=N.enter().append("g").attr("class","nvd3 nv-wrap nv-stackedAreaChart").append("g"),P=N.select("g");if(O.append("rect").style("opacity",0),O.append("g").attr("class","nv-x nv-axis"),O.append("g").attr("class","nv-y nv-axis"),O.append("g").attr("class","nv-stackedWrap"),O.append("g").attr("class","nv-legendWrap"),O.append("g").attr("class","nv-controlsWrap"),O.append("g").attr("class","nv-interactive"),P.select("rect").attr("width",K).attr("height",L),q){var Q=p?K-z:K;h.width(Q),P.select(".nv-legendWrap").datum(k).call(h),l.top!=h.height()&&(l.top=h.height(),L=a.utils.availableHeight(n,x,l)),P.select(".nv-legendWrap").attr("transform","translate("+(K-Q)+","+-l.top+")")}if(p){var R=[{key:B.stacked||"Stacked",metaKey:"Stacked",disabled:"stack"!=e.style(),style:"stack"},{key:B.stream||"Stream",metaKey:"Stream",disabled:"stream"!=e.style(),style:"stream"},{key:B.expanded||"Expanded",metaKey:"Expanded",disabled:"expand"!=e.style(),style:"expand"},{key:B.stack_percent||"Stack %",metaKey:"Stack_Percent",disabled:"stack_percent"!=e.style(),style:"stack_percent"}];z=A.length/3*260,R=R.filter(function(a){return-1!==A.indexOf(a.metaKey)}),i.width(z).color(["#444","#444","#444"]),P.select(".nv-controlsWrap").datum(R).call(i),l.top!=Math.max(i.height(),h.height())&&(l.top=Math.max(i.height(),h.height()),L=a.utils.availableHeight(n,x,l)),P.select(".nv-controlsWrap").attr("transform","translate(0,"+-l.top+")")}N.attr("transform","translate("+l.left+","+l.top+")"),t&&P.select(".nv-y.nv-axis").attr("transform","translate("+K+",0)"),u&&(j.width(K).height(L).margin({left:l.left,top:l.top}).svgContainer(x).xScale(c),N.select(".nv-interactive").call(j)),e.width(K).height(L);var S=P.select(".nv-stackedWrap").datum(k);if(S.transition().call(e),r&&(f.scale(c)._ticks(a.utils.calcTicksX(K/100,k)).tickSize(-L,0),P.select(".nv-x.nv-axis").attr("transform","translate(0,"+L+")"),P.select(".nv-x.nv-axis").transition().duration(0).call(f)),s){var T;if(T="wiggle"===e.offset()?0:a.utils.calcTicksY(L/36,k),g.scale(d)._ticks(T).tickSize(-K,0),"expand"===e.style()||"stack_percent"===e.style()){var U=g.tickFormat();D&&U===J||(D=U),g.tickFormat(J)}else D&&(g.tickFormat(D),D=null);P.select(".nv-y.nv-axis").transition().duration(0).call(g)}e.dispatch.on("areaClick.toggle",function(a){k.forEach(1===k.filter(function(a){return!a.disabled}).length?function(a){a.disabled=!1}:function(b,c){b.disabled=c!=a.seriesIndex}),v.disabled=k.map(function(a){return!!a.disabled}),y.stateChange(v),b.update()}),h.dispatch.on("stateChange",function(a){for(var c in a)v[c]=a[c];y.stateChange(v),b.update()}),i.dispatch.on("legendClick",function(a){a.disabled&&(R=R.map(function(a){return a.disabled=!0,a}),a.disabled=!1,e.style(a.style),v.style=e.style(),y.stateChange(v),b.update())}),j.dispatch.on("elementMousemove",function(c){e.clearHighlights();var d,g,h,i=[];if(k.filter(function(a,b){return a.seriesIndex=b,!a.disabled}).forEach(function(f,j){g=a.interactiveBisect(f.values,c.pointXValue,b.x());var k=f.values[g],l=b.y()(k,g);if(null!=l&&e.highlightPoint(j,g,!0),"undefined"!=typeof k){"undefined"==typeof d&&(d=k),"undefined"==typeof h&&(h=b.xScale()(b.x()(k,g)));var m="expand"==e.style()?k.display.y:b.y()(k,g);i.push({key:f.key,value:m,color:o(f,f.seriesIndex),stackedValue:k.display})}}),i.reverse(),i.length>2){var m=b.yScale().invert(c.mouseY),n=null;i.forEach(function(a,b){m=Math.abs(m);var c=Math.abs(a.stackedValue.y0),d=Math.abs(a.stackedValue.y);return m>=c&&d+c>=m?void(n=b):void 0}),null!=n&&(i[n].highlight=!0)}var p=f.tickFormat()(b.x()(d,g)),q=j.tooltip.valueFormatter();"expand"===e.style()||"stack_percent"===e.style()?(E||(E=q),q=d3.format(".1%")):E&&(q=E,E=null),j.tooltip.position({left:h+l.left,top:c.mouseY+l.top}).chartContainer(F.parentNode).valueFormatter(q).data({value:p,series:i})(),j.renderGuideLine(h)}),j.dispatch.on("elementMouseout",function(){e.clearHighlights()}),y.on("changeState",function(a){"undefined"!=typeof a.disabled&&k.length===a.disabled.length&&(k.forEach(function(b,c){b.disabled=a.disabled[c]}),v.disabled=a.disabled),"undefined"!=typeof a.style&&(e.style(a.style),G=a.style),b.update()})}),F.renderEnd("stacked Area chart immediate"),b}var c,d,e=a.models.stackedArea(),f=a.models.axis(),g=a.models.axis(),h=a.models.legend(),i=a.models.legend(),j=a.interactiveGuideline(),k=a.models.tooltip(),l={top:30,right:25,bottom:50,left:60},m=null,n=null,o=a.utils.defaultColor(),p=!0,q=!0,r=!0,s=!0,t=!1,u=!1,v=a.utils.state(),w=null,x=null,y=d3.dispatch("stateChange","changeState","renderEnd"),z=250,A=["Stacked","Stream","Expanded"],B={},C=250;v.style=e.style(),f.orient("bottom").tickPadding(7),g.orient(t?"right":"left"),k.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)}),j.tooltip.headerFormatter(function(a,b){return f.tickFormat()(a,b)}).valueFormatter(function(a,b){return g.tickFormat()(a,b)});var D=null,E=null;i.updateState(!1);var F=a.utils.renderWatch(y),G=e.style(),H=function(a){return function(){return{active:a.map(function(a){return!a.disabled}),style:e.style()}}},I=function(a){return function(b){void 0!==b.style&&(G=b.style),void 0!==b.active&&a.forEach(function(a,c){a.disabled=!b.active[c]})}},J=d3.format("%");return e.dispatch.on("elementMouseover.tooltip",function(a){a.point.x=e.x()(a.point),a.point.y=e.y()(a.point),k.data(a).position(a.pos).hidden(!1)}),e.dispatch.on("elementMouseout.tooltip",function(){k.hidden(!0)}),b.dispatch=y,b.stacked=e,b.legend=h,b.controls=i,b.xAxis=f,b.yAxis=g,b.interactiveLayer=j,b.tooltip=k,b.dispatch=y,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return m},set:function(a){m=a}},height:{get:function(){return n},set:function(a){n=a}},showLegend:{get:function(){return q},set:function(a){q=a}},showXAxis:{get:function(){return r},set:function(a){r=a}},showYAxis:{get:function(){return s},set:function(a){s=a}},defaultState:{get:function(){return w},set:function(a){w=a}},noData:{get:function(){return x},set:function(a){x=a}},showControls:{get:function(){return p},set:function(a){p=a}},controlLabels:{get:function(){return B},set:function(a){B=a}},controlOptions:{get:function(){return A},set:function(a){A=a}},tooltips:{get:function(){return k.enabled()},set:function(b){a.deprecated("tooltips","use chart.tooltip.enabled() instead"),k.enabled(!!b)}},tooltipContent:{get:function(){return k.contentGenerator()},set:function(b){a.deprecated("tooltipContent","use chart.tooltip.contentGenerator() instead"),k.contentGenerator(b)}},margin:{get:function(){return l},set:function(a){l.top=void 0!==a.top?a.top:l.top,l.right=void 0!==a.right?a.right:l.right,l.bottom=void 0!==a.bottom?a.bottom:l.bottom,l.left=void 0!==a.left?a.left:l.left}},duration:{get:function(){return C},set:function(a){C=a,F.reset(C),e.duration(C),f.duration(C),g.duration(C)}},color:{get:function(){return o},set:function(b){o=a.utils.getColor(b),h.color(o),e.color(o)}},rightAlignYAxis:{get:function(){return t},set:function(a){t=a,g.orient(t?"right":"left")}},useInteractiveGuideline:{get:function(){return u},set:function(a){u=!!a,b.interactive(!a),b.useVoronoi(!a),e.scatter.interactive(!a)}}}),a.utils.inheritOptions(b,e),a.utils.initOptions(b),b},a.models.sunburst=function(){"use strict";function b(u){return t.reset(),u.each(function(b){function t(a){a.x0=a.x,a.dx0=a.dx}function u(a){var b=d3.interpolate(p.domain(),[a.x,a.x+a.dx]),c=d3.interpolate(q.domain(),[a.y,1]),d=d3.interpolate(q.range(),[a.y?20:0,y]);return function(a,e){return e?function(){return s(a)}:function(e){return p.domain(b(e)),q.domain(c(e)).range(d(e)),s(a)}}}l=d3.select(this);var v,w=a.utils.availableWidth(g,l,f),x=a.utils.availableHeight(h,l,f),y=Math.min(w,x)/2;a.utils.initSVG(l);var z=l.selectAll(".nv-wrap.nv-sunburst").data(b),A=z.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburst nv-chart-"+k),B=A.selectAll("nv-sunburst");z.attr("transform","translate("+w/2+","+x/2+")"),l.on("click",function(a,b){o.chartClick({data:a,index:b,pos:d3.event,id:k})}),q.range([0,y]),c=c||b,e=b[0],r.value(j[i]||j.count),v=B.data(r.nodes).enter().append("path").attr("d",s).style("fill",function(a){return m((a.children?a:a.parent).name)}).style("stroke","#FFF").on("click",function(a){d!==c&&c!==a&&(d=c),c=a,v.transition().duration(n).attrTween("d",u(a))}).each(t).on("dblclick",function(a){d.parent==a&&v.transition().duration(n).attrTween("d",u(e))}).each(t).on("mouseover",function(a){d3.select(this).classed("hover",!0).style("opacity",.8),o.elementMouseover({data:a,color:d3.select(this).style("fill")})}).on("mouseout",function(a){d3.select(this).classed("hover",!1).style("opacity",1),o.elementMouseout({data:a})}).on("mousemove",function(a){o.elementMousemove({data:a})})}),t.renderEnd("sunburst immediate"),b}var c,d,e,f={top:0,right:0,bottom:0,left:0},g=null,h=null,i="count",j={count:function(){return 1},size:function(a){return a.size}},k=Math.floor(1e4*Math.random()),l=null,m=a.utils.defaultColor(),n=500,o=d3.dispatch("chartClick","elementClick","elementDblClick","elementMousemove","elementMouseover","elementMouseout","renderEnd"),p=d3.scale.linear().range([0,2*Math.PI]),q=d3.scale.sqrt(),r=d3.layout.partition().sort(null).value(function(){return 1}),s=d3.svg.arc().startAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x)))}).endAngle(function(a){return Math.max(0,Math.min(2*Math.PI,p(a.x+a.dx)))}).innerRadius(function(a){return Math.max(0,q(a.y))}).outerRadius(function(a){return Math.max(0,q(a.y+a.dy))}),t=a.utils.renderWatch(o);return b.dispatch=o,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{width:{get:function(){return g},set:function(a){g=a}},height:{get:function(){return h},set:function(a){h=a}},mode:{get:function(){return i},set:function(a){i=a}},id:{get:function(){return k},set:function(a){k=a}},duration:{get:function(){return n},set:function(a){n=a}},margin:{get:function(){return f},set:function(a){f.top=void 0!=a.top?a.top:f.top,f.right=void 0!=a.right?a.right:f.right,f.bottom=void 0!=a.bottom?a.bottom:f.bottom,f.left=void 0!=a.left?a.left:f.left}},color:{get:function(){return m},set:function(b){m=a.utils.getColor(b)}}}),a.utils.initOptions(b),b},a.models.sunburstChart=function(){"use strict";function b(d){return m.reset(),m.models(c),d.each(function(d){var h=d3.select(this);a.utils.initSVG(h);var i=a.utils.availableWidth(f,h,e),j=a.utils.availableHeight(g,h,e);if(b.update=function(){0===k?h.call(b):h.transition().duration(k).call(b)},b.container=this,!d||!d.length)return a.utils.noData(b,h),b;h.selectAll(".nv-noData").remove();var l=h.selectAll("g.nv-wrap.nv-sunburstChart").data(d),m=l.enter().append("g").attr("class","nvd3 nv-wrap nv-sunburstChart").append("g"),n=l.select("g");m.append("g").attr("class","nv-sunburstWrap"),l.attr("transform","translate("+e.left+","+e.top+")"),c.width(i).height(j);var o=n.select(".nv-sunburstWrap").datum(d);d3.transition(o).call(c)}),m.renderEnd("sunburstChart immediate"),b}var c=a.models.sunburst(),d=a.models.tooltip(),e={top:30,right:20,bottom:20,left:20},f=null,g=null,h=a.utils.defaultColor(),i=(Math.round(1e5*Math.random()),null),j=null,k=250,l=d3.dispatch("tooltipShow","tooltipHide","stateChange","changeState","renderEnd"),m=a.utils.renderWatch(l);return d.headerEnabled(!1).duration(0).valueFormatter(function(a){return a}),c.dispatch.on("elementMouseover.tooltip",function(a){a.series={key:a.data.name,value:a.data.size,color:a.color},d.data(a).hidden(!1)}),c.dispatch.on("elementMouseout.tooltip",function(){d.hidden(!0)}),c.dispatch.on("elementMousemove.tooltip",function(){d.position({top:d3.event.pageY,left:d3.event.pageX})()}),b.dispatch=l,b.sunburst=c,b.tooltip=d,b.options=a.utils.optionsFunc.bind(b),b._options=Object.create({},{noData:{get:function(){return j},set:function(a){j=a}},defaultState:{get:function(){return i},set:function(a){i=a}},color:{get:function(){return h},set:function(a){h=a,c.color(h)}},duration:{get:function(){return k},set:function(a){k=a,m.reset(k),c.duration(k)}},margin:{get:function(){return e},set:function(a){e.top=void 0!==a.top?a.top:e.top,e.right=void 0!==a.right?a.right:e.right,e.bottom=void 0!==a.bottom?a.bottom:e.bottom,e.left=void 0!==a.left?a.left:e.left}}}),a.utils.inheritOptions(b,c),a.utils.initOptions(b),b},a.version="1.8.1"}();/*! - * Bootstrap v4.1.3 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,h){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
      ',trigger:"hover focus",title:"",delay:0,html:!(Ie={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Se={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},we="out",Ne={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:(De="show")+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Oe="fade",ke="show",Pe=".tooltip-inner",je=".arrow",He="hover",Le="focus",Re="click",xe="manual",We=function(){function i(t,e){if("undefined"==typeof h)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=pe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(pe(this.getTipElement()).hasClass(ke))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),pe.removeData(this.element,this.constructor.DATA_KEY),pe(this.element).off(this.constructor.EVENT_KEY),pe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&pe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===pe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=pe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){pe(this.element).trigger(t);var n=pe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Fn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&pe(i).addClass(Oe);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:pe(document).find(this.config.container);pe(i).data(this.constructor.DATA_KEY,this),pe.contains(this.element.ownerDocument.documentElement,this.tip)||pe(i).appendTo(a),pe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new h(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),pe(i).addClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().on("mouseover",null,pe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,pe(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(pe(this.tip).hasClass(Oe)){var c=Fn.getTransitionDurationFromElement(this.tip);pe(this.tip).one(Fn.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=pe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),pe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(pe(this.element).trigger(i),!i.isDefaultPrevented()){if(pe(n).removeClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().off("mouseover",null,pe.noop),this._activeTrigger[Re]=!1,this._activeTrigger[Le]=!1,this._activeTrigger[He]=!1,pe(this.tip).hasClass(Oe)){var o=Fn.getTransitionDurationFromElement(n);pe(n).one(Fn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){pe(this.getTipElement()).addClass(Te+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||pe(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(pe(t.querySelectorAll(Pe)),this.getTitle()),pe(t).removeClass(Oe+" "+ke)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?pe(e).parent().is(t)||t.empty().append(e):t.text(pe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return Ie[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)pe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==xe){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;pe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}pe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Le:He]=!0),pe(e.getTipElement()).hasClass(ke)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Le:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,pe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Fn.typeCheckConfig(ve,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=pe(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(pe(t).removeClass(Oe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=pe(this).data(ye),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),pe(this).data(ye,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Ae}},{key:"NAME",get:function(){return ve}},{key:"DATA_KEY",get:function(){return ye}},{key:"Event",get:function(){return Ne}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}(),pe.fn[ve]=We._jQueryInterface,pe.fn[ve].Constructor=We,pe.fn[ve].noConflict=function(){return pe.fn[ve]=Ce,We._jQueryInterface},We),Jn=(qe="popover",Ke="."+(Fe="bs.popover"),Me=(Ue=e).fn[qe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},zn.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},zn.DefaultType,{content:"(string|element|function)"}),ze="fade",Ze=".popover-header",Ge=".popover-body",$e={HIDE:"hide"+Ke,HIDDEN:"hidden"+Ke,SHOW:(Je="show")+Ke,SHOWN:"shown"+Ke,INSERTED:"inserted"+Ke,CLICK:"click"+Ke,FOCUSIN:"focusin"+Ke,FOCUSOUT:"focusout"+Ke,MOUSEENTER:"mouseenter"+Ke,MOUSELEAVE:"mouseleave"+Ke},Xe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Ue(this.getTipElement()).addClass(Qe+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Ue(this.config.template)[0],this.tip},r.setContent=function(){var t=Ue(this.getTipElement());this.setElementContent(t.find(Ze),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ge),e),t.removeClass(ze+" "+Je)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Ue(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||tn?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++ie;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.5371385*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.get(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var n,t=oa,e=1/0;t;)t.c?(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++aa;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}function l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:function(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonStart(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r===A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s),v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)0?0:3:xo(r[0]-e)0?2:1:xo(r[1]-t)0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=Math.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,Gt(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){ -r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Ze(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)Uo?{x:s,y:xo(t-s)Uo?{x:xo(e-g)Uo?{x:h,y:xo(t-h)Uo?{x:xo(e-p)=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.yd||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.yr||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.yp){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.xu||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return ur;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.rotate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++oe;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.ro;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++0;h--)o.push(u(c)*h);for(c=0;o[c]l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n):""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++oe?[NaN,NaN]:[e>0?a[e-1]:n[0],et?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.count,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ir&&(e=r)}else{for(;++i=r){e=r;break}for(;++ir&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i=r){e=r;break}for(;++ie&&(e=r)}else{for(;++i=r){e=r;break}for(;++ie&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}else{for(;++u=r){e=i=r;break}for(;++ur&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++ii){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++rr;++r)g[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++uu;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ua.forEach(function(n,t){ua.set(n,Mn(t))}),ao.functor=En,ao.xhr=An(m),ao.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var o=Cn(n,t,null==e?r:i(e),u);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:i(n)):e},o}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++f;){var r=n.charCodeAt(f++),a=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(r!==l)continue;return n.slice(t,f-a)}return n.slice(t)}for(var r,i,u={},o={},a=[],c=n.length,f=0,s=0;(r=e())!==o;){for(var h=[];r!==u&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,s++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new y,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(u).join("\n")},e},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var oa,aa,la,ca,fa=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){qn.apply(this,arguments)},ao.timer.flush=function(){Rn(),Dn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var sa=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Un);ao.formatPrefix=function(n,t){var e=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,Pn(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),sa[8+e/3]};var ha=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,pa=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,Pn(n,t))).toFixed(Math.max(0,Math.min(20,Pn(n*(1+1e-15),t))))}}),ga=ao.time={},va=Date;Hn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){da.setUTCDate.apply(this._,arguments)},setDay:function(){da.setUTCDay.apply(this._,arguments)},setFullYear:function(){da.setUTCFullYear.apply(this._,arguments)},setHours:function(){da.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){da.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){da.setUTCMinutes.apply(this._,arguments)},setMonth:function(){da.setUTCMonth.apply(this._,arguments)},setSeconds:function(){da.setUTCSeconds.apply(this._,arguments)},setTime:function(){da.setTime.apply(this._,arguments)}};var da=Date.prototype;ga.year=On(function(n){return n=ga.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ga.years=ga.year.range,ga.years.utc=ga.year.utc.range,ga.day=On(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ga.days=ga.day.range,ga.days.utc=ga.day.utc.range,ga.dayOfYear=function(n){var t=ga.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ga[n]=On(function(n){return(n=ga.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ga[n+"s"]=e.range,ga[n+"s"].utc=e.utc.range,ga[n+"OfYear"]=function(n){var e=ga.year(n).getDay();return Math.floor((ga.dayOfYear(n)+(e+t)%7)/7)}}),ga.week=ga.sunday,ga.weeks=ga.sunday.range,ga.weeks.utc=ga.sunday.utc.range,ga.weekOfYear=ga.sundayOfYear;var ya={"-":"",_:" ",0:"0"},ma=/^\s*\d+/,Ma=/^%/;ao.locale=function(n){return{numberFormat:jn(n),timeFormat:Yn(n)}};var xa=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"], -shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=xa.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,ba),st(ba.s,this.s,this),this.s?this.t+=ba.t:this.s=ba.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var ba=new ft;ao.geo.stream=function(n,t){n&&_a.hasOwnProperty(n.type)?_a[n.type](n,t):ht(n,t)};var _a={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++rn?4*Fo+n:n,Na.lineStart=Na.lineEnd=Na.point=b}};ao.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,e){var r=dt([t*Yo,e*Yo]);if(y){var i=mt(y,r),u=[i[1],-i[0],0],o=mt(u,i);bt(o),o=_t(o);var l=t-g,c=l>0?1:-1,v=o[0]*Zo*c,d=xo(l)>180;if(d^(v>c*g&&c*t>v)){var m=o[1]*Zo;m>p&&(p=m)}else if(v=(v+360)%360-180,d^(v>c*g&&c*t>v)){var m=-o[1]*Zo;s>m&&(s=m)}else s>e&&(s=e),e>p&&(p=e);d?g>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>g?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,e);y=r,g=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,y=null}function i(n,e){if(y){var r=n-g;m+=xo(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Na.point(n,e),t(n,e)}function u(){Na.lineStart()}function o(){i(v,d),Na.lineEnd(),xo(m)>Uo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function l(n,t){return n[0]-t[0]}function c(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nka?(f=-(h=180),s=-(p=90)):m>Uo?p=90:-Uo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),M=[],ao.geo.stream(n,b);var t=M.length;if(t){M.sort(l);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],c(e[0],i)||c(e[1],i)?(a(i[0],e[1])>a(i[0],i[1])&&(i[1]=e[1]),a(e[0],i[1])>a(i[0],i[1])&&(i[0]=e[0])):u.push(i=e);for(var o,e,g=-(1/0),t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(o=a(i[1],e[0]))>g&&(g=o,f=e[0],h=i[1])}return M=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Ea=Aa=Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,ja);var t=Da,e=Pa,r=Ua,i=t*t+e*e+r*r;return jo>i&&(t=qa,e=Ta,r=Ra,Uo>Aa&&(t=Ca,e=za,r=La),i=t*t+e*e+r*r,jo>i)?[NaN,NaN]:[Math.atan2(e,t)*Zo,tn(r/Math.sqrt(i))*Zo]};var Ea,Aa,Ca,za,La,qa,Ta,Ra,Da,Pa,Ua,ja={sphere:b,point:St,lineStart:Nt,lineEnd:Et,polygonStart:function(){ja.lineStart=At},polygonEnd:function(){ja.lineStart=Nt}},Fa=Rt(zt,jt,Ht,[-Fo,-Fo/2]),Ha=1e9;ao.geo.clipExtent=function(){var n,t,e,r,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Zt(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Vt(Xt)}).raw=Xt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,e(u,o),t||(r(u,o),t)||i(u,o),t}var t,e,r,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),l={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?o:i>=.166&&.234>i&&r>=-.214&&-.115>r?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var c=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*c,s-.238*c],[f+.455*c,s+.238*c]]).stream(l).point,r=o.translate([f-.307*c,s+.201*c]).clipExtent([[f-.425*c+Uo,s+.12*c+Uo],[f-.214*c-Uo,s+.234*c-Uo]]).stream(l).point,i=a.translate([f-.205*c,s+.212*c]).clipExtent([[f-.214*c+Uo,s+.166*c+Uo],[f-.115*c-Uo,s+.234*c-Uo]]).stream(l).point,n},n.scale(1070)};var Oa,Ia,Ya,Za,Va,Xa,$a={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Ia=0,$a.lineStart=$t},polygonEnd:function(){$a.lineStart=$a.lineEnd=$a.point=b,Oa+=xo(Ia/2)}},Ba={point:Bt,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Wa={point:Gt,lineStart:Kt,lineEnd:Qt,polygonStart:function(){Wa.lineStart=ne},polygonEnd:function(){Wa.point=Gt,Wa.lineStart=Kt,Wa.lineEnd=Qt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var e,r,i,u,o,a=4.5;return n.area=function(n){return Oa=0,ao.geo.stream(n,i($a)),Oa},n.centroid=function(n){return Ca=za=La=qa=Ta=Ra=Da=Pa=Ua=0,ao.geo.stream(n,i(Wa)),Ua?[Da/Ua,Pa/Ua]:Ra?[qa/Ra,Ta/Ra]:La?[Ca/La,za/La]:[NaN,NaN]},n.bounds=function(n){return Va=Xa=-(Ya=Za=1/0),ao.geo.stream(n,i(Ba)),[[Ya,Za],[Va,Xa]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||re(n):m,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new Wt:new te(n),"function"!=typeof a&&u.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var e=new ie(t);for(var r in n)e[r]=n[r];return e}}},ie.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=oe,ao.geo.projectionMutator=ae,(ao.geo.equirectangular=function(){return oe(ce)}).raw=ce.invert=ce,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t}return n=se(n[0]%360*Yo,n[1]*Yo,n.length>2?n[2]*Yo:0),t.invert=function(t){return t=n.invert(t[0]*Yo,t[1]*Yo),t[0]*=Zo,t[1]*=Zo,t},t},fe.invert=ce,ao.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=se(-n[0]*Yo,-n[1]*Yo,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Zo,n[1]*=Zo}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=ve((t=+r)*Yo,i*Yo),n):t},n.precision=function(r){return arguments.length?(e=ve(t*Yo,(i=+r)*Yo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Yo,i=n[1]*Yo,u=t[1]*Yo,o=Math.sin(r),a=Math.cos(r),l=Math.sin(i),c=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*o)*e+(e=c*f-l*s*a)*e),l*f+c*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(c/y)*y,l,y).map(p)).concat(ao.range(Math.ceil(r/g)*g,e,g).filter(function(n){return xo(n%d)>Uo}).map(f)).concat(ao.range(Math.ceil(a/v)*v,o,v).filter(function(n){return xo(n%y)>Uo}).map(s))}var e,r,i,u,o,a,l,c,f,s,h,p,g=10,v=g,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(l).slice(1),h(i).reverse().slice(1),p(c).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],c=+t[0][1],l=+t[1][1],u>i&&(t=u,u=i,i=t),c>l&&(t=c,c=l,l=t),n.precision(m)):[[u,c],[i,l]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(g=+t[0],v=+t[1],n):[g,v]},n.precision=function(t){return arguments.length?(m=+t,f=ye(a,o,90),s=me(r,e,m),h=ye(c,l,90),p=me(u,i,m),n):m},n.majorExtent([[-180,-90+Uo],[180,90-Uo]]).minorExtent([[-180,-80-Uo],[180,80+Uo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Me,i=xe;return n.distance=function(){return ao.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return be(n[0]*Yo,n[1]*Yo,t[0]*Yo,t[1]*Yo)},ao.geo.length=function(n){return Ja=0,ao.geo.stream(n,Ga),Ja};var Ja,Ga={sphere:b,point:b,lineStart:_e,lineEnd:b,polygonStart:b,polygonEnd:b},Ka=we(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return oe(Ka)}).raw=Ka;var Qa=we(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return oe(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Vt(Se)}).raw=Se,(ao.geo.conicEquidistant=function(){return Vt(ke)}).raw=ke;var nl=we(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return oe(nl)}).raw=nl,Ne.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Io]},(ao.geo.mercator=function(){return Ee(Ne)}).raw=Ne;var tl=we(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return oe(tl)}).raw=tl;var el=we(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return oe(el)}).raw=el,Ae.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Io]},(ao.geo.transverseMercator=function(){var n=Ee(Ae),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Ae,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=En(e),u=En(r),o=n.length,a=[],l=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(qe),t=0;o>t;t++)l.push([a[t][0],-a[t][1]]);var c=Le(a),f=Le(l),s=f[0]===c[0],h=f[f.length-1]===c[c.length-1],p=[];for(t=c.length-1;t>=0;--t)p.push(n[a[c[t]][2]]);for(t=+s;t=r&&c.x<=u&&c.y>=i&&c.y<=o?[[r,o],[u,o],[u,i],[r,i]]:[];f.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Uo)*Uo,y:Math.round(o(n,t)/Uo)*Uo,i:t}})}var r=Ce,i=ze,u=r,o=i,a=sl;return n?t(n):(t.links=function(n){return ar(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ar(e(n)).cells.forEach(function(e,r){for(var i,u,o=e.site,a=e.edges.sort(Ve),l=-1,c=a.length,f=a[c-1].edge,s=f.l===o?f.r:f.l;++l=c,h=r>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=hr()),s?i=c:a=c,h?o=f:l=f,u(n,t,e,r,i,o,a,l)}var f,s,h,p,g,v,d,y,m,M=En(a),x=En(l);if(null!=t)v=t,d=e,y=r,m=i;else if(y=m=-(v=d=1/0),s=[],h=[],g=n.length,o)for(p=0;g>p;++p)f=n[p],f.xy&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;g>p;++p){var b=+M(f=n[p],p),_=+x(f,p);v>b&&(v=b),d>_&&(d=_),b>y&&(y=b),_>m&&(m=_),s.push(b),h.push(_)}var w=y-v,S=m-d;w>S?m=d+w:y=v+S;var k=hr();if(k.add=function(n){u(k,n,+M(n,++p),+x(n,p),v,d,y,m)},k.visit=function(n){pr(n,k,v,d,y,m)},k.find=function(n){return gr(k,n[0],n[1],v,d,y,m)},p=-1,null==t){for(;++p=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=vl.get(e)||gl,r=dl.get(r)||m,br(r(e.apply(null,lo.call(arguments,1))))},ao.interpolateHcl=Rr,ao.interpolateHsl=Dr,ao.interpolateLab=Pr,ao.interpolateRound=Ur,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new jr(e?e.matrix:yl)})(n)},jr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var yl={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=$r,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/y){if(v>l){var c=t.charge/l;n.px-=u*c,n.py-=o*c}return!0}if(t.point&&l&&v>l){var c=t.pointCharge/l;n.px-=u*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,l.resume()}var e,r,i,u,o,a,l={},c=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=ml,p=Ml,g=-30,v=xl,d=.1,y=.64,M=[],x=[];return l.tick=function(){if((i*=.99)<.005)return e=null,c.end({type:"end",alpha:i=0}),!0;var t,r,l,h,p,v,y,m,b,_=M.length,w=x.length;for(r=0;w>r;++r)l=x[r],h=l.source,p=l.target,m=p.x-h.x,b=p.y-h.y,(v=m*m+b*b)&&(v=i*o[r]*((v=Math.sqrt(v))-u[r])/v,m*=v,b*=v,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=b*y,h.x+=m*(y=1-y),h.y+=b*y);if((y=i*d)&&(m=f[0]/2,b=f[1]/2,r=-1,y))for(;++r<_;)l=M[r],l.x+=(m-l.x)*y,l.y+=(b-l.y)*y;if(g)for(ri(t=ao.geom.quadtree(M),i,a),r=-1;++r<_;)(l=M[r]).fixed||t.visit(n(l));for(r=-1;++r<_;)l=M[r],l.fixed?(l.x=l.px,l.y=l.py):(l.x-=(l.px-(l.px=l.x))*s,l.y-=(l.py-(l.py=l.y))*s);c.tick({type:"tick",alpha:i})},l.nodes=function(n){return arguments.length?(M=n,l):M},l.links=function(n){return arguments.length?(x=n,l):x},l.size=function(n){return arguments.length?(f=n,l):f},l.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,l):h},l.distance=l.linkDistance,l.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,l):p},l.friction=function(n){return arguments.length?(s=+n,l):s},l.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,l):g},l.chargeDistance=function(n){return arguments.length?(v=n*n,l):Math.sqrt(v)},l.gravity=function(n){return arguments.length?(d=+n,l):d},l.theta=function(n){return arguments.length?(y=n*n,l):Math.sqrt(y)},l.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(e.c=null,e.t=NaN,e=null,c.end({type:"end",alpha:i=0})):n>0&&(c.start({type:"start",alpha:i=n}),e=qn(l.tick)),l):i},l.start=function(){function n(n,r){if(!e){for(e=new Array(i),l=0;i>l;++l)e[l]=[];for(l=0;c>l;++l){var u=x[l];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var o,a=e[t],l=-1,f=a.length;++lt;++t)(r=M[t]).index=t,r.weight=0;for(t=0;c>t;++t)r=x[t],"number"==typeof r.source&&(r.source=M[r.source]),"number"==typeof r.target&&(r.target=M[r.target]),++r.source.weight,++r.target.weight;for(t=0;i>t;++t)r=M[t],isNaN(r.x)&&(r.x=n("x",s)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof h)for(t=0;c>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;c>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;c>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;c>t;++t)o[t]=p;if(a=[],"function"==typeof g)for(t=0;i>t;++t)a[t]=+g.call(this,M[t],t);else for(t=0;i>t;++t)a[t]=g;return l.resume()},l.resume=function(){return l.alpha(.1)},l.stop=function(){return l.alpha(0)},l.drag=function(){return r||(r=ao.behavior.drag().origin(m).on("dragstart.force",Qr).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ei).call(r):r},ao.rebind(l,c,"on")};var ml=20,Ml=1,xl=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(c=e.call(n,u,u.depth))&&(l=c.length)){for(var l,c,f;--l>=0;)o.push(f=c[l]),f.parent=u,f.depth=u.depth+1;r&&(u.value=0),u.children=c}else r&&(u.value=+r.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var e,i;t&&(e=n.children)&&e.sort(t),r&&(i=n.parent)&&(i.value+=n.value)}),a}var t=ci,e=ai,r=li;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(o=u.length)){var o,a,l,c=-1;for(r=t.value?r/t.value:0;++cs?-1:1),g=ao.sum(c),v=g?(s-l*p)/g:0,d=ao.range(l),y=[];return null!=e&&d.sort(e===bl?function(n,t){return c[t]-c[n]}:function(n,t){return e(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=c[n],startAngle:f,endAngle:f+=a*v+p,padAngle:h}}),y}var t=Number,e=bl,r=0,i=Ho,u=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var bl={};ao.layout.stack=function(){function n(a,l){if(!(h=a.length))return a;var c=a.map(function(e,r){return t.call(n,e,r)}),f=c.map(function(t){return t.map(function(t,e){return[u.call(n,t,e),o.call(n,t,e)]})}),s=e.call(n,f,l);c=ao.permute(c,s),f=ao.permute(f,s);var h,p,g,v,d=r.call(n,f,l),y=c[0].length;for(g=0;y>g;++g)for(i.call(n,c[0][g],v=d[g],f[0][g][1]),p=1;h>p;++p)i.call(n,c[p][g],v+=f[p-1][g][1],f[p][g][1]);return a}var t=m,e=gi,r=vi,i=pi,u=si,o=hi;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:_l.get(t)||gi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:wl.get(t)||vi,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var _l=ao.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(di),u=n.map(yi),o=ao.range(r).sort(function(n,t){return i[n]-i[t]}),a=0,l=0,c=[],f=[];for(t=0;r>t;++t)e=o[t],l>a?(a+=u[e],c.push(e)):(l+=u[e],f.push(e));return f.reverse().concat(c)},reverse:function(n){return ao.range(n.length).reverse()},"default":gi}),wl=ao.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,o=[],a=0,l=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;u>e;++e)l[e]=(a-o[e])/2;return l},wiggle:function(n){var t,e,r,i,u,o,a,l,c,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=l=c=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,a=s[e][0]-s[e-1][0];f>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;u+=o*n[t][e][1]}p[e]=l-=i?u/i*a:0,c>l&&(c=l)}for(e=0;h>e;++e)p[e]-=c;return p},expand:function(n){var t,e,r,i=n.length,u=n[0].length,o=1/i,a=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=o}for(e=0;u>e;++e)a[e]=0;return a},zero:vi});ao.layout.histogram=function(){function n(n,u){for(var o,a,l=[],c=n.map(e,this),f=r.call(this,c,u),s=i.call(this,f,c,u),u=-1,h=c.length,p=s.length-1,g=t?1:1/h;++u0)for(u=-1;++u=f[0]&&a<=f[1]&&(o=l[ao.bisect(s,a,1,p)-1],o.y+=g,o.push(n[u]));return l}var t=!0,e=Number,r=bi,i=Mi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=En(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:En(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ao.layout.pack=function(){function n(n,u){var o=e.call(this,n,u),a=o[0],l=i[0],c=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ni),r){var s=r*(t?1:Math.max(2*a.r/l,2*a.r/c))/2;oi(a,function(n){n.r+=s}),oi(a,Ni),oi(a,function(n){n.r-=s})}return Ci(a,l/2,c/2,t?1:1/Math.max(2*a.r/l,2*a.r/c)),o}var t,e=ao.layout.hierarchy().sort(_i),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},ii(n,e)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,e),h.parent.m=-h.z,ui(h,r),c)ui(s,u);else{var p=s,g=s,v=s;ui(s,function(n){n.xg.x&&(g=n),n.depth>v.depth&&(v=n)});var d=a(p,g)/2-p.x,y=l[0]/(g.x+a(g,p)/2+d),m=l[1]/(v.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)r.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Di(n);var u=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-u):n.z=u}else r&&(n.z=r.z+a(n._,r._));n.parent.A=i(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,e){if(t){for(var r,i=n,u=n,o=t,l=i.parent.children[0],c=i.m,f=u.m,s=o.m,h=l.m;o=Ti(o),i=qi(i),o&&i;)l=qi(l),u=Ti(u),u.a=n,r=o.z+s-i.z-c+a(o._,i._),r>0&&(Ri(Pi(o,n,e),n,r),c+=r,f+=r),s+=o.m,c+=i.m,h+=l.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!qi(l)&&(l.t=i,l.m+=c-h,e=n)}return e}function u(n){n.x*=l[0],n.y=n.depth*l[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=Li,l=[1,1],c=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(c=null==(l=t)?u:null,n):c?null:l},n.nodeSize=function(t){return arguments.length?(c=null==(l=t)?null:u,n):c?l:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),l=a[0],c=0;oi(l,function(n){var t=n.children;t&&t.length?(n.x=ji(t),n.y=Ui(t)):(n.x=o?c+=e(n,o):0,n.y=0,o=n)});var f=Fi(l),s=Hi(l),h=f.x-e(f,s)/2,p=s.x+e(s,f)/2;return oi(l,i?function(n){n.x=(n.x-l.x)*r[0],n.y=(l.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(p-h)*r[0],n.y=(1-(l.y?n.y/l.y:1))*r[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),e=Li,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++it?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var o,a,l,c=s(e),f=[],h=u.slice(),g=1/0,v="slice"===p?c.dx:"dice"===p?c.dy:"slice-dice"===p?1&e.depth?c.dy:c.dx:Math.min(c.dx,c.dy);for(n(h,c.dx*c.dy/e.value),f.area=0;(l=h.length)>0;)f.push(o=h[l-1]),f.area+=o.area,"squarify"!==p||(a=r(f,v))<=g?(h.pop(),g=a):(f.area-=f.pop().area,i(f,v,c,!1),v=Math.min(c.dx,c.dy),f.length=f.area=0,g=1/0);f.length&&(i(f,v,c,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,o=s(t),a=r.slice(),l=[];for(n(a,o.dx*o.dy/t.value),l.area=0;u=a.pop();)l.push(u),l.area+=u.area,null!=u.z&&(i(l,u.z?o.dx:o.dy,o,!a.length),l.length=l.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,o=-1,a=n.length;++oe&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*g/r,r/(t*u*g)):1/0}function i(n,t,e,r){var i,u=-1,o=n.length,a=e.x,c=e.y,f=t?l(n.area/t):0; -if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++ue.dx)&&(f=e.dx);++ue&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ao.scale={};var Sl={floor:m,ceil:m};ao.scale.linear=function(){return Wi([0,1],[0,1],Mr,!1)};var kl={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return ru(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Nl=ao.format(".0e"),El={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Al)},ao.scale.category20=function(){return ao.scale.ordinal().range(Cl)},ao.scale.category20b=function(){return ao.scale.ordinal().range(zl)},ao.scale.category20c=function(){return ao.scale.ordinal().range(Ll)};var Al=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Cl=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),zl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),Ll=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return lu(0,1,[0,1])},ao.scale.threshold=function(){return cu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),c=Math.max(0,+r.apply(this,arguments)),f=o.apply(this,arguments)-Io,s=a.apply(this,arguments)-Io,h=Math.abs(s-f),p=f>s?0:1;if(n>c&&(g=c,c=n,n=g),h>=Oo)return t(c,p)+(n?t(n,1-p):"")+"Z";var g,v,d,y,m,M,x,b,_,w,S,k,N=0,E=0,A=[];if((y=(+l.apply(this,arguments)||0)/2)&&(d=u===ql?Math.sqrt(n*n+c*c):+u.apply(this,arguments),p||(E*=-1),c&&(E=tn(d/c*Math.sin(y))),n&&(N=tn(d/n*Math.sin(y)))),c){m=c*Math.cos(f+E),M=c*Math.sin(f+E),x=c*Math.cos(s-E),b=c*Math.sin(s-E);var C=Math.abs(s-f-2*E)<=Fo?0:1;if(E&&yu(m,M,x,b)===p^C){var z=(f+s)/2;m=c*Math.cos(z),M=c*Math.sin(z),x=b=null}}else m=M=0;if(n){_=n*Math.cos(s-N),w=n*Math.sin(s-N),S=n*Math.cos(f+N),k=n*Math.sin(f+N);var L=Math.abs(f-s+2*N)<=Fo?0:1;if(N&&yu(_,w,S,k)===1-p^L){var q=(f+s)/2;_=n*Math.cos(q),w=n*Math.sin(q),S=k=null}}else _=w=0;if(h>Uo&&(g=Math.min(Math.abs(c-n)/2,+i.apply(this,arguments)))>.001){v=c>n^p?0:1;var T=g,R=g;if(Fo>h){var D=null==S?[_,w]:null==x?[m,M]:Re([m,M],[S,k],[x,b],[_,w]),P=m-D[0],U=M-D[1],j=x-D[0],F=b-D[1],H=1/Math.sin(Math.acos((P*j+U*F)/(Math.sqrt(P*P+U*U)*Math.sqrt(j*j+F*F)))/2),O=Math.sqrt(D[0]*D[0]+D[1]*D[1]);R=Math.min(g,(n-O)/(H-1)),T=Math.min(g,(c-O)/(H+1))}if(null!=x){var I=mu(null==S?[_,w]:[S,k],[m,M],c,T,p),Y=mu([x,b],[_,w],c,T,p);g===T?A.push("M",I[0],"A",T,",",T," 0 0,",v," ",I[1],"A",c,",",c," 0 ",1-p^yu(I[1][0],I[1][1],Y[1][0],Y[1][1]),",",p," ",Y[1],"A",T,",",T," 0 0,",v," ",Y[0]):A.push("M",I[0],"A",T,",",T," 0 1,",v," ",Y[0])}else A.push("M",m,",",M);if(null!=S){var Z=mu([m,M],[S,k],n,-R,p),V=mu([_,w],null==x?[m,M]:[x,b],n,-R,p);g===R?A.push("L",V[0],"A",R,",",R," 0 0,",v," ",V[1],"A",n,",",n," 0 ",p^yu(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-p," ",Z[1],"A",R,",",R," 0 0,",v," ",Z[0]):A.push("L",V[0],"A",R,",",R," 0 0,",v," ",Z[0])}else A.push("L",_,",",w)}else A.push("M",m,",",M),null!=x&&A.push("A",c,",",c," 0 ",C,",",p," ",x,",",b),A.push("L",_,",",w),null!=S&&A.push("A",n,",",n," 0 ",L,",",1-p," ",S,",",k);return A.push("Z"),A.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=hu,r=pu,i=su,u=ql,o=gu,a=vu,l=du;return n.innerRadius=function(t){return arguments.length?(e=En(t),n):e},n.outerRadius=function(t){return arguments.length?(r=En(t),n):r},n.cornerRadius=function(t){return arguments.length?(i=En(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==ql?ql:En(t),n):u},n.startAngle=function(t){return arguments.length?(o=En(t),n):o},n.endAngle=function(t){return arguments.length?(a=En(t),n):a},n.padAngle=function(t){return arguments.length?(l=En(t),n):l},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Io;return[Math.cos(t)*n,Math.sin(t)*n]},n};var ql="auto";ao.svg.line=function(){return Mu(m)};var Tl=ao.map({linear:xu,"linear-closed":bu,step:_u,"step-before":wu,"step-after":Su,basis:zu,"basis-open":Lu,"basis-closed":qu,bundle:Tu,cardinal:Eu,"cardinal-open":ku,"cardinal-closed":Nu,monotone:Fu});Tl.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Rl=[0,2/3,1/3,0],Dl=[0,1/3,2/3,0],Pl=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=Mu(Hu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=Su,Su.reverse=wu,ao.svg.area=function(){return Ou(m)},ao.svg.area.radial=function(){var n=Ou(Hu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var l=t(this,u,n,a),c=t(this,o,n,a);return"M"+l.p0+r(l.r,l.p1,l.a1-l.a0)+(e(l,c)?i(l.r,l.p1,l.r,l.p0):i(l.r,l.p1,c.r,c.p0)+r(c.r,c.p1,c.a1-c.a0)+i(c.r,c.p1,l.r,l.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=a.call(n,i,r),o=l.call(n,i,r)-Io,f=c.call(n,i,r)-Io;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Fo)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Me,o=xe,a=Iu,l=gu,c=vu;return n.radius=function(t){return arguments.length?(a=En(t),n):a},n.source=function(t){return arguments.length?(u=En(t),n):u},n.target=function(t){return arguments.length?(o=En(t),n):o},n.startAngle=function(t){return arguments.length?(l=En(t),n):l},n.endAngle=function(t){return arguments.length?(c=En(t),n):c},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=e.call(this,n,i),a=(u.y+o.y)/2,l=[u,{x:u.x,y:a},{x:o.x,y:a},o];return l=l.map(r),"M"+l[0]+"C"+l[1]+" "+l[2]+" "+l[3]}var t=Me,e=xe,r=Yu;return n.source=function(e){return arguments.length?(t=En(e),n):t},n.target=function(t){return arguments.length?(e=En(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Yu,e=n.projection;return n.projection=function(n){return arguments.length?e(Zu(t=n)):t},n},ao.svg.symbol=function(){function n(n,r){return(Ul.get(t.call(this,n,r))||$u)(e.call(this,n,r))}var t=Xu,e=Vu;return n.type=function(e){return arguments.length?(t=En(e),n):t},n.size=function(t){return arguments.length?(e=En(t),n):e},n};var Ul=ao.map({circle:$u,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Fl)),e=t*Fl;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/jl),e=t*jl/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ao.svg.symbolTypes=Ul.keys();var jl=Math.sqrt(3),Fl=Math.tan(30*Yo);Co.transition=function(n){for(var t,e,r=Hl||++Zl,i=Ku(n),u=[],o=Ol||{time:Date.now(),ease:Nr,delay:0,duration:250},a=-1,l=this.length;++au;u++){i.push(t=[]);for(var e=this[u],a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return Wu(i,this.namespace,this.id)},Yl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(i){i[r][e].tween.set(n,t)})},Yl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?$r:Mr,a=ao.ns.qualify(n);return Ju(this,"attr."+n,t,a.local?u:i)},Yl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Yl.style=function(n,e,r){function i(){this.style.removeProperty(n)}function u(e){return null==e?i:(e+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==e&&(i=Mr(u,e),function(t){this.style.setProperty(n,i(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Ju(this,"style."+n,e,u)},Yl.styleTween=function(n,e,r){function i(i,u){var o=e.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,i)},Yl.text=function(n){return Ju(this,"text",n,Gu)},Yl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Yl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),Y(this,function(r){r[e][t].ease=n}))},Yl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,i,u){r[e][t].delay=+n.call(r,r.__data__,i,u)}:(n=+n,function(r){r[e][t].delay=n}))},Yl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,i,u){r[e][t].duration=Math.max(1,n.call(r,r.__data__,i,u))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Yl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var i=Ol,u=Hl;try{Hl=e,Y(this,function(t,i,u){Ol=t[r][e],n.call(t,t.__data__,i,u)})}finally{Ol=i,Hl=u}}else Y(this,function(i){var u=i[r][e];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Yl.transition=function(){for(var n,t,e,r,i=this.id,u=++Zl,o=this.namespace,a=[],l=0,c=this.length;c>l;l++){a.push(n=[]);for(var t=this[l],f=0,s=t.length;s>f;f++)(e=t[f])&&(r=e[o][i],Qu(e,f,o,u,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Wu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,c=ao.select(this),f=this.__chart__||e,s=this.__chart__=e.copy(),h=null==l?s.ticks?s.ticks.apply(s,a):s.domain():l,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,g=c.selectAll(".tick").data(h,s),v=g.enter().insert("g",".domain").attr("class","tick").style("opacity",Uo),d=ao.transition(g.exit()).style("opacity",Uo).remove(),y=ao.transition(g.order()).style("opacity",1),M=Math.max(i,0)+o,x=Zi(s),b=c.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ao.transition(b));v.append("line"),v.append("text");var w,S,k,N,E=v.select("line"),A=y.select("line"),C=g.select("text").text(p),z=v.select("text"),L=y.select("text"),q="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=no,w="x",k="y",S="x2",N="y2",C.attr("dy",0>q?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+q*u+"V0H"+x[1]+"V"+q*u)):(n=to,w="y",k="x",S="y2",N="x2",C.attr("dy",".32em").style("text-anchor",0>q?"end":"start"),_.attr("d","M"+q*u+","+x[0]+"H0V"+x[1]+"H"+q*u)),E.attr(N,q*i),z.attr(k,q*M),A.attr(S,0).attr(N,q*i),L.attr(w,0).attr(k,q*M),s.rangeBand){var T=s,R=T.rangeBand()/2;f=s=function(n){return T(n)+R}}else f.rangeBand?f=s:d.call(n,s,f);v.call(n,f,s),y.call(n,s,s)})}var t,e=ao.scale.linear(),r=Vl,i=6,u=6,o=3,a=[10],l=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Xl?t+"":Vl,n):r},n.ticks=function(){return arguments.length?(a=co(arguments),n):a},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(i=+t,u=+arguments[e-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Vl="bottom",Xl={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return $l[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,s=ao.transition(t),h=ao.transition(o);c&&(l=Zi(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),r(s)),f&&(l=Zi(f),h.attr("y",l[0]).attr("height",l[1]-l[0]),i(s)),e(s)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(C||(M=null,L[0]-=s[1],L[1]-=h[1],C=2),S())}function v(){32==ao.event.keyCode&&2==C&&(L[0]+=s[1],L[1]+=h[1],C=0,S())}function d(){var n=ao.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ao.event.altKey?(M||(M=[(s[0]+s[1])/2,(h[0]+h[1])/2]),L[0]=s[+(n[0]f?(i=r,r=f):i=f),v[0]!=r||v[1]!=i?(e?a=null:o=null,v[0]=r,v[1]=i,!0):void 0}function m(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ao.select(ao.event.target),w=l.of(b,arguments),k=ao.select(b),N=_.datum(),E=!/^(n|s)$/.test(N)&&c,A=!/^(e|w)$/.test(N)&&f,C=_.classed("extent"),z=W(b),L=ao.mouse(b),q=ao.select(t(b)).on("keydown.brush",u).on("keyup.brush",v);if(ao.event.changedTouches?q.on("touchmove.brush",d).on("touchend.brush",m):q.on("mousemove.brush",d).on("mouseup.brush",m),k.interrupt().selectAll("*").interrupt(),C)L[0]=s[0]-L[0],L[1]=h[0]-L[1];else if(N){var T=+/w$/.test(N),R=+/^n/.test(N);x=[s[1-T]-L[0],h[1-R]-L[1]],L[0]=s[T],L[1]=h[R]}else ao.event.altKey&&(M=L.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,l=N(n,"brushstart","brush","brushend"),c=null,f=null,s=[0,0],h=[0,0],p=!0,g=!0,v=Bl[0];return n.event=function(n){n.each(function(){var n=l.of(this,arguments),t={x:s,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Hl?ao.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,s=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=xr(s,t.x),r=xr(h,t.y);return o=a=null,function(i){s=t.x=e(i),h=t.y=r(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,v=Bl[!c<<1|!f],n):c},n.y=function(t){return arguments.length?(f=t,v=Bl[!c<<1|!f],n):f},n.clamp=function(t){return arguments.length?(c&&f?(p=!!t[0],g=!!t[1]):c?p=!!t:f&&(g=!!t),n):c&&f?[p,g]:c?p:f?g:null},n.extent=function(t){var e,r,i,u,l;return arguments.length?(c&&(e=t[0],r=t[1],f&&(e=e[0],r=r[0]),o=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(l=e,e=r,r=l),e==s[0]&&r==s[1]||(s=[e,r])),f&&(i=t[0],u=t[1],c&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(l=i,i=u,u=l),i==h[0]&&u==h[1]||(h=[i,u])),n):(c&&(o?(e=o[0],r=o[1]):(e=s[0],r=s[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(l=e,e=r,r=l))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(l=i,i=u,u=l))),c&&f?[[e,i],[r,u]]:c?[e,r]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!c&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,l,"on")};var $l={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Bl=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Wl=ga.format=xa.timeFormat,Jl=Wl.utc,Gl=Jl("%Y-%m-%dT%H:%M:%S.%LZ");Wl.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?eo:Gl,eo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},eo.toString=Gl.toString,ga.second=On(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ga.seconds=ga.second.range,ga.seconds.utc=ga.second.utc.range,ga.minute=On(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ga.minutes=ga.minute.range,ga.minutes.utc=ga.minute.utc.range,ga.hour=On(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ga.hours=ga.hour.range,ga.hours.utc=ga.hour.utc.range,ga.month=On(function(n){return n=ga.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ga.months=ga.month.range,ga.months.utc=ga.month.utc.range;var Kl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Ql=[[ga.second,1],[ga.second,5],[ga.second,15],[ga.second,30],[ga.minute,1],[ga.minute,5],[ga.minute,15],[ga.minute,30],[ga.hour,1],[ga.hour,3],[ga.hour,6],[ga.hour,12],[ga.day,1],[ga.day,2],[ga.week,1],[ga.month,1],[ga.month,3],[ga.year,1]],nc=Wl.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",zt]]),tc={range:function(n,t,e){return ao.range(Math.ceil(n/e)*e,+t,e).map(io)},floor:m,ceil:m};Ql.year=ga.year,ga.scale=function(){return ro(ao.scale.linear(),Ql,nc)};var ec=Ql.map(function(n){return[n[0].utc,n[1]]}),rc=Jl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",zt]]);ec.year=ga.year.utc,ga.scale.utc=function(){return ro(ao.scale.linear(),ec,rc)},ao.text=An(function(n){return n.responseText}),ao.json=function(n,t){return Cn(n,"application/json",uo,t)},ao.html=function(n,t){return Cn(n,"text/html",oo,t)},ao.xml=An(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}();/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
      "],col:[2,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" - - - - - -
      -
      - {{percent}}% covered ({{level}}) -
      -
      - - - - - Dashboard for {{full_path}} - - - - - - - -
      -
      -
      -
      - -
      -
      -
      -
      -
      -
      -
      -

      Classes

      -
      -
      -
      -
      -

      Coverage Distribution

      -
      - -
      -
      -
      -

      Complexity

      -
      - -
      -
      -
      -
      -
      -

      Insufficient Coverage

      -
      - - - - - - - - -{{insufficient_coverage_classes}} - -
      ClassCoverage
      -
      -
      -
      -

      Project Risks

      -
      - - - - - - - - -{{project_risks_classes}} - -
      ClassCRAP
      -
      -
      -
      -
      -
      -

      Methods

      -
      -
      -
      -
      -

      Coverage Distribution

      -
      - -
      -
      -
      -

      Complexity

      -
      - -
      -
      -
      -
      -
      -

      Insufficient Coverage

      -
      - - - - - - - - -{{insufficient_coverage_methods}} - -
      MethodCoverage
      -
      -
      -
      -

      Project Risks

      -
      - - - - - - - - -{{project_risks_methods}} - -
      MethodCRAP
      -
      -
      -
      - -
      - - - - - - - - - - - Code Coverage for {{full_path}} - - - - - - - -
      -
      -
      -
      - -
      -
      -
      -
      -
      -
      - - - - - - - - - - - - - - -{{items}} - -
       
      Code Coverage
       
      Lines
      Functions and Methods
      Classes and Traits
      -
      -
      -
      -

      Legend

      -

      - Low: 0% to {{low_upper_bound}}% - Medium: {{low_upper_bound}}% to {{high_lower_bound}}% - High: {{high_lower_bound}}% to 100% -

      -

      - Generated by php-code-coverage {{version}} using {{runtime}}{{generator}} at {{date}}. -

      -
      -
      - - - - {{name}} - {{methods_bar}} -
      {{methods_tested_percent}}
      -
      {{methods_number}}
      - {{crap}} - {{lines_bar}} -
      {{lines_executed_percent}}
      -
      {{lines_number}}
      - - -.octicon { - display: inline-block; - vertical-align: text-top; - fill: currentColor; -} -/*! - * Bootstrap v4.1.3 (https://getbootstrap.com/) - * Copyright 2011-2018 The Bootstrap Authors - * Copyright 2011-2018 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014 \00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label::after,.was-validated .custom-file-input:valid~.custom-file-label::after{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label::after,.was-validated .custom-file-input:invalid~.custom-file-label::after{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#007bff;background-color:transparent;background-image:none;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;background-color:transparent;background-image:none;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;background-color:transparent}.btn-link:hover{color:#0056b3;text-decoration:underline;background-color:transparent;border-color:transparent}.btn-link.focus,.btn-link:focus{text-decoration:underline;border-color:transparent;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group,.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:active~.custom-control-label::before{color:#fff;background-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background-repeat:no-repeat;background-position:center center;background-size:50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::before{background-color:#007bff}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(128,189,255,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:75%}.custom-select-lg{height:calc(2.875rem + 2px);padding-top:.375rem;padding-bottom:.375rem;font-size:125%}.custom-file{position:relative;display:inline-block;width:100%;height:calc(2.25rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(2.25rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:focus~.custom-file-label::after{border-color:#80bdff}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(2.25rem + 2px);padding:.375rem .75rem;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:2.25rem;padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;padding-left:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child){border-radius:0}.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#fff;text-decoration:none;background-color:#0062cc}.badge-secondary{color:#fff;background-color:#6c757d}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#545b62}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{color:#000;text-decoration:none;opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-25%);transform:translate(0,-25%)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:translate(0,0);transform:translate(0,0)}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - (.5rem * 2))}.modal-dialog-centered::before{display:block;height:calc(100vh - (.5rem * 2));content:""}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - (1.75rem * 2))}.modal-dialog-centered::before{height:calc(100vh - (1.75rem * 2))}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::after,.bs-popover-top .arrow::before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow::before,.bs-popover-top .arrow::before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow::after,.bs-popover-top .arrow::after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::after,.bs-popover-right .arrow::before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow::before,.bs-popover-right .arrow::before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow::after,.bs-popover-right .arrow::after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::after,.bs-popover-bottom .arrow::before{border-width:0 .5rem .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow::before,.bs-popover-bottom .arrow::before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow::after,.bs-popover-bottom .arrow::after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::after,.bs-popover-left .arrow::before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow::before,.bs-popover-left .arrow::before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow::after,.bs-popover-left .arrow::after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-ms-flex-align:center;align-items:center;width:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-item-next,.carousel-item-prev,.carousel-item.active{transition:none}}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-fade .carousel-item{opacity:0;transition-duration:.6s;transition-property:opacity}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{opacity:0}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat center center;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:rgba(255,255,255,.5)}.carousel-indicators li::before{position:absolute;top:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li::after{position:absolute;bottom:-10px;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0062cc!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#545b62!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} -/*# sourceMappingURL=bootstrap.min.css.map */ -body { - padding-top: 10px; -} - -.popover { - max-width: none; -} - -.octicon { - margin-right:.25em; -} - -.table-bordered>thead>tr>td { - border-bottom-width: 1px; -} - -.table tbody>tr>td, .table thead>tr>td { - padding-top: 3px; - padding-bottom: 3px; -} - -.table-condensed tbody>tr>td { - padding-top: 0; - padding-bottom: 0; -} - -.table .progress { - margin-bottom: inherit; -} - -.table-borderless th, .table-borderless td { - border: 0 !important; -} - -.table tbody tr.covered-by-large-tests, li.covered-by-large-tests, tr.success, td.success, li.success, span.success { - background-color: #dff0d8; -} - -.table tbody tr.covered-by-medium-tests, li.covered-by-medium-tests { - background-color: #c3e3b5; -} - -.table tbody tr.covered-by-small-tests, li.covered-by-small-tests { - background-color: #99cb84; -} - -.table tbody tr.danger, .table tbody td.danger, li.danger, span.danger { - background-color: #f2dede; -} - -.table tbody td.warning, li.warning, span.warning { - background-color: #fcf8e3; -} - -.table tbody td.info { - background-color: #d9edf7; -} - -td.big { - width: 117px; -} - -td.small { -} - -td.codeLine { - font-family: "Source Code Pro", "SFMono-Regular", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - white-space: pre; -} - -td span.comment { - color: #888a85; -} - -td span.default { - color: #2e3436; -} - -td span.html { - color: #888a85; -} - -td span.keyword { - color: #2e3436; - font-weight: bold; -} - -pre span.string { - color: #2e3436; -} - -span.success, span.warning, span.danger { - margin-right: 2px; - padding-left: 10px; - padding-right: 10px; - text-align: center; -} - -#classCoverageDistribution, #classComplexity { - height: 200px; - width: 475px; -} - -#toplink { - position: fixed; - left: 5px; - bottom: 5px; - outline: 0; -} - -svg text { - font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; - font-size: 11px; - color: #666; - fill: #666; -} - -.scrollbox { - height:245px; - overflow-x:hidden; - overflow-y:scroll; -} -.nvd3 .nv-axis{pointer-events:none;opacity:1}.nvd3 .nv-axis path{fill:none;stroke:#000;stroke-opacity:.75;shape-rendering:crispEdges}.nvd3 .nv-axis path.domain{stroke-opacity:.75}.nvd3 .nv-axis.nv-x path.domain{stroke-opacity:0}.nvd3 .nv-axis line{fill:none;stroke:#e5e5e5;shape-rendering:crispEdges}.nvd3 .nv-axis .zero line,.nvd3 .nv-axis line.zero{stroke-opacity:.75}.nvd3 .nv-axis .nv-axisMaxMin text{font-weight:700}.nvd3 .x .nv-axis .nv-axisMaxMin text,.nvd3 .x2 .nv-axis .nv-axisMaxMin text,.nvd3 .x3 .nv-axis .nv-axisMaxMin text{text-anchor:middle}.nvd3 .nv-axis.nv-disabled{opacity:0}.nvd3 .nv-bars rect{fill-opacity:.75;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-bars rect.hover{fill-opacity:1}.nvd3 .nv-bars .hover rect{fill:#add8e6}.nvd3 .nv-bars text{fill:rgba(0,0,0,0)}.nvd3 .nv-bars .hover text{fill:rgba(0,0,0,1)}.nvd3 .nv-multibar .nv-groups rect,.nvd3 .nv-multibarHorizontal .nv-groups rect,.nvd3 .nv-discretebar .nv-groups rect{stroke-opacity:0;transition:fill-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear}.nvd3 .nv-multibar .nv-groups rect:hover,.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,.nvd3 .nv-candlestickBar .nv-ticks rect:hover,.nvd3 .nv-discretebar .nv-groups rect:hover{fill-opacity:1}.nvd3 .nv-discretebar .nv-groups text,.nvd3 .nv-multibarHorizontal .nv-groups text{font-weight:700;fill:rgba(0,0,0,1);stroke:rgba(0,0,0,0)}.nvd3 .nv-boxplot circle{fill-opacity:.5}.nvd3 .nv-boxplot circle:hover{fill-opacity:1}.nvd3 .nv-boxplot rect:hover{fill-opacity:1}.nvd3 line.nv-boxplot-median{stroke:#000}.nv-boxplot-tick:hover{stroke-width:2.5px}.nvd3.nv-bullet{font:10px sans-serif}.nvd3.nv-bullet .nv-measure{fill-opacity:.8}.nvd3.nv-bullet .nv-measure:hover{fill-opacity:1}.nvd3.nv-bullet .nv-marker{stroke:#000;stroke-width:2px}.nvd3.nv-bullet .nv-markerTriangle{stroke:#000;fill:#fff;stroke-width:1.5px}.nvd3.nv-bullet .nv-tick line{stroke:#666;stroke-width:.5px}.nvd3.nv-bullet .nv-range.nv-s0{fill:#eee}.nvd3.nv-bullet .nv-range.nv-s1{fill:#ddd}.nvd3.nv-bullet .nv-range.nv-s2{fill:#ccc}.nvd3.nv-bullet .nv-title{font-size:14px;font-weight:700}.nvd3.nv-bullet .nv-subtitle{fill:#999}.nvd3.nv-bullet .nv-range{fill:#bababa;fill-opacity:.4}.nvd3.nv-bullet .nv-range:hover{fill-opacity:.7}.nvd3.nv-candlestickBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.positive rect{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-candlestickBar .nv-ticks .nv-tick.negative rect{stroke:#d62728;fill:#d62728}.with-transitions .nv-candlestickBar .nv-ticks .nv-tick{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-candlestickBar .nv-ticks line{stroke:#333}.nvd3 .nv-legend .nv-disabled rect{}.nvd3 .nv-check-box .nv-box{fill-opacity:0;stroke-width:2}.nvd3 .nv-check-box .nv-check{fill-opacity:0;stroke-width:4}.nvd3 .nv-series.nv-disabled .nv-check-box .nv-check{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-controlsWrap .nv-legend .nv-check-box .nv-check{opacity:0}.nvd3.nv-linePlusBar .nv-bar rect{fill-opacity:.75}.nvd3.nv-linePlusBar .nv-bar rect:hover{fill-opacity:1}.nvd3 .nv-groups path.nv-line{fill:none}.nvd3 .nv-groups path.nv-area{stroke:none}.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point{fill-opacity:0;stroke-opacity:0}.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point{fill-opacity:.5!important;stroke-opacity:.5!important}.with-transitions .nvd3 .nv-groups .nv-point{transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-scatter .nv-groups .nv-point.hover,.nvd3 .nv-groups .nv-point.hover{stroke-width:7px;fill-opacity:.95!important;stroke-opacity:.95!important}.nvd3 .nv-point-paths path{stroke:#aaa;stroke-opacity:0;fill:#eee;fill-opacity:0}.nvd3 .nv-indexLine{cursor:ew-resize}svg.nvd3-svg{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-ms-user-select:none;-moz-user-select:none;user-select:none;display:block;width:100%;height:100%}.nvtooltip.with-3d-shadow,.with-3d-shadow .nvtooltip{-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.nvd3 text{font:400 12px Arial}.nvd3 .title{font:700 14px Arial}.nvd3 .nv-background{fill:#fff;fill-opacity:0}.nvd3.nv-noData{font-size:18px;font-weight:700}.nv-brush .extent{fill-opacity:.125;shape-rendering:crispEdges}.nv-brush .resize path{fill:#eee;stroke:#666}.nvd3 .nv-legend .nv-series{cursor:pointer}.nvd3 .nv-legend .nv-disabled circle{fill-opacity:0}.nvd3 .nv-brush .extent{fill-opacity:0!important}.nvd3 .nv-brushBackground rect{stroke:#000;stroke-width:.4;fill:#fff;fill-opacity:.7}.nvd3.nv-ohlcBar .nv-ticks .nv-tick{stroke-width:1px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover{stroke-width:2px}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive{stroke:#2ca02c}.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative{stroke:#d62728}.nvd3 .background path{fill:none;stroke:#EEE;stroke-opacity:.4;shape-rendering:crispEdges}.nvd3 .foreground path{fill:none;stroke-opacity:.7}.nvd3 .nv-parallelCoordinates-brush .extent{fill:#fff;fill-opacity:.6;stroke:gray;shape-rendering:crispEdges}.nvd3 .nv-parallelCoordinates .hover{fill-opacity:1;stroke-width:3px}.nvd3 .missingValuesline line{fill:none;stroke:#000;stroke-width:1;stroke-opacity:1;stroke-dasharray:5,5}.nvd3.nv-pie path{stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-width 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-pie .nv-pie-title{font-size:24px;fill:rgba(19,196,249,.59)}.nvd3.nv-pie .nv-slice text{stroke:#000;stroke-width:0}.nvd3.nv-pie path{stroke:#fff;stroke-width:1px;stroke-opacity:1}.nvd3.nv-pie .hover path{fill-opacity:.7}.nvd3.nv-pie .nv-label{pointer-events:none}.nvd3.nv-pie .nv-label rect{fill-opacity:0;stroke-opacity:0}.nvd3 .nv-groups .nv-point.hover{stroke-width:20px;stroke-opacity:.5}.nvd3 .nv-scatter .nv-point.hover{fill-opacity:1}.nv-noninteractive{pointer-events:none}.nv-distx,.nv-disty{pointer-events:none}.nvd3.nv-sparkline path{fill:none}.nvd3.nv-sparklineplus g.nv-hoverValue{pointer-events:none}.nvd3.nv-sparklineplus .nv-hoverValue line{stroke:#333;stroke-width:1.5px}.nvd3.nv-sparklineplus,.nvd3.nv-sparklineplus g{pointer-events:all}.nvd3 .nv-hoverArea{fill-opacity:0;stroke-opacity:0}.nvd3.nv-sparklineplus .nv-xValue,.nvd3.nv-sparklineplus .nv-yValue{stroke-width:0;font-size:.9em;font-weight:400}.nvd3.nv-sparklineplus .nv-yValue{stroke:#f66}.nvd3.nv-sparklineplus .nv-maxValue{stroke:#2ca02c;fill:#2ca02c}.nvd3.nv-sparklineplus .nv-minValue{stroke:#d62728;fill:#d62728}.nvd3.nv-sparklineplus .nv-currentValue{font-weight:700;font-size:1.1em}.nvd3.nv-stackedarea path.nv-area{fill-opacity:.7;stroke-opacity:0;transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-moz-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear;-webkit-transition:fill-opacity 250ms linear,stroke-opacity 250ms linear}.nvd3.nv-stackedarea path.nv-area.hover{fill-opacity:.9}.nvd3.nv-stackedarea .nv-groups .nv-point{stroke-opacity:0;fill-opacity:0}.nvtooltip{position:absolute;background-color:rgba(255,255,255,1);color:rgba(0,0,0,1);padding:1px;border:1px solid rgba(0,0,0,.2);z-index:10000;display:block;font-family:Arial;font-size:13px;text-align:left;pointer-events:none;white-space:nowrap;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.nvtooltip{background:rgba(255,255,255,.8);border:1px solid rgba(0,0,0,.5);border-radius:4px}.nvtooltip.with-transitions,.with-transitions .nvtooltip{transition:opacity 50ms linear;-moz-transition:opacity 50ms linear;-webkit-transition:opacity 50ms linear;transition-delay:200ms;-moz-transition-delay:200ms;-webkit-transition-delay:200ms}.nvtooltip.x-nvtooltip,.nvtooltip.y-nvtooltip{padding:8px}.nvtooltip h3{margin:0;padding:4px 14px;line-height:18px;font-weight:400;background-color:rgba(247,247,247,.75);color:rgba(0,0,0,1);text-align:center;border-bottom:1px solid #ebebeb;-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0}.nvtooltip p{margin:0;padding:5px 14px;text-align:center}.nvtooltip span{display:inline-block;margin:2px 0}.nvtooltip table{margin:6px;border-spacing:0}.nvtooltip table td{padding:2px 9px 2px 0;vertical-align:middle}.nvtooltip table td.key{font-weight:400}.nvtooltip table td.value{text-align:right;font-weight:700}.nvtooltip table tr.highlight td{padding:1px 9px 1px 0;border-bottom-style:solid;border-bottom-width:1px;border-top-style:solid;border-top-width:1px}.nvtooltip table td.legend-color-guide div{width:8px;height:8px;vertical-align:middle}.nvtooltip table td.legend-color-guide div{width:12px;height:12px;border:1px solid #999}.nvtooltip .footer{padding:3px;text-align:center}.nvtooltip-pending-removal{pointer-events:none;display:none}.nvd3 .nv-interactiveGuideLine{pointer-events:none}.nvd3 line.nv-guideline{stroke:#ccc} - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\Node\File as FileNode; -use SebastianBergmann\CodeCoverage\Util; - -/** - * Renders a file node. - */ -final class File extends Renderer -{ - /** - * @var int - */ - private $htmlSpecialCharsFlags = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE; - - /** - * @throws \RuntimeException - */ - public function render(FileNode $node, string $file): void - { - $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}'); - - $template->setVar( - [ - 'items' => $this->renderItems($node), - 'lines' => $this->renderSource($node), - ] - ); - - $this->setCommonTemplateVariables($template, $node); - - $template->renderTo($file); - } - - protected function renderItems(FileNode $node): string - { - $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}'); - - $methodItemTemplate = new \Text_Template( - $this->templatePath . 'method_item.html', - '{{', - '}}' - ); - - $items = $this->renderItemTemplate( - $template, - [ - 'name' => 'Total', - 'numClasses' => $node->getNumClassesAndTraits(), - 'numTestedClasses' => $node->getNumTestedClassesAndTraits(), - 'numMethods' => $node->getNumFunctionsAndMethods(), - 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(), - 'linesExecutedPercent' => $node->getLineExecutedPercent(false), - 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(), - 'numExecutedLines' => $node->getNumExecutedLines(), - 'numExecutableLines' => $node->getNumExecutableLines(), - 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false), - 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(), - 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false), - 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(), - 'crap' => 'CRAP', - ] - ); - - $items .= $this->renderFunctionItems( - $node->getFunctions(), - $methodItemTemplate - ); - - $items .= $this->renderTraitOrClassItems( - $node->getTraits(), - $template, - $methodItemTemplate - ); - - $items .= $this->renderTraitOrClassItems( - $node->getClasses(), - $template, - $methodItemTemplate - ); - - return $items; - } - - protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate): string - { - $buffer = ''; - - if (empty($items)) { - return $buffer; - } - - foreach ($items as $name => $item) { - $numMethods = 0; - $numTestedMethods = 0; - - foreach ($item['methods'] as $method) { - if ($method['executableLines'] > 0) { - $numMethods++; - - if ($method['executedLines'] === $method['executableLines']) { - $numTestedMethods++; - } - } - } - - if ($item['executableLines'] > 0) { - $numClasses = 1; - $numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0; - $linesExecutedPercentAsString = Util::percent( - $item['executedLines'], - $item['executableLines'], - true - ); - } else { - $numClasses = 'n/a'; - $numTestedClasses = 'n/a'; - $linesExecutedPercentAsString = 'n/a'; - } - - $buffer .= $this->renderItemTemplate( - $template, - [ - 'name' => $this->abbreviateClassName($name), - 'numClasses' => $numClasses, - 'numTestedClasses' => $numTestedClasses, - 'numMethods' => $numMethods, - 'numTestedMethods' => $numTestedMethods, - 'linesExecutedPercent' => Util::percent( - $item['executedLines'], - $item['executableLines'], - false - ), - 'linesExecutedPercentAsString' => $linesExecutedPercentAsString, - 'numExecutedLines' => $item['executedLines'], - 'numExecutableLines' => $item['executableLines'], - 'testedMethodsPercent' => Util::percent( - $numTestedMethods, - $numMethods - ), - 'testedMethodsPercentAsString' => Util::percent( - $numTestedMethods, - $numMethods, - true - ), - 'testedClassesPercent' => Util::percent( - $numTestedMethods == $numMethods ? 1 : 0, - 1 - ), - 'testedClassesPercentAsString' => Util::percent( - $numTestedMethods == $numMethods ? 1 : 0, - 1, - true - ), - 'crap' => $item['crap'], - ] - ); - - foreach ($item['methods'] as $method) { - $buffer .= $this->renderFunctionOrMethodItem( - $methodItemTemplate, - $method, - ' ' - ); - } - } - - return $buffer; - } - - protected function renderFunctionItems(array $functions, \Text_Template $template): string - { - if (empty($functions)) { - return ''; - } - - $buffer = ''; - - foreach ($functions as $function) { - $buffer .= $this->renderFunctionOrMethodItem( - $template, - $function - ); - } - - return $buffer; - } - - protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, string $indent = ''): string - { - $numMethods = 0; - $numTestedMethods = 0; - - if ($item['executableLines'] > 0) { - $numMethods = 1; - - if ($item['executedLines'] === $item['executableLines']) { - $numTestedMethods = 1; - } - } - - return $this->renderItemTemplate( - $template, - [ - 'name' => \sprintf( - '%s%s', - $indent, - $item['startLine'], - \htmlspecialchars($item['signature'], $this->htmlSpecialCharsFlags), - $item['functionName'] ?? $item['methodName'] - ), - 'numMethods' => $numMethods, - 'numTestedMethods' => $numTestedMethods, - 'linesExecutedPercent' => Util::percent( - $item['executedLines'], - $item['executableLines'] - ), - 'linesExecutedPercentAsString' => Util::percent( - $item['executedLines'], - $item['executableLines'], - true - ), - 'numExecutedLines' => $item['executedLines'], - 'numExecutableLines' => $item['executableLines'], - 'testedMethodsPercent' => Util::percent( - $numTestedMethods, - 1 - ), - 'testedMethodsPercentAsString' => Util::percent( - $numTestedMethods, - 1, - true - ), - 'crap' => $item['crap'], - ] - ); - } - - protected function renderSource(FileNode $node): string - { - $coverageData = $node->getCoverageData(); - $testData = $node->getTestData(); - $codeLines = $this->loadFile($node->getPath()); - $lines = ''; - $i = 1; - - foreach ($codeLines as $line) { - $trClass = ''; - $popoverContent = ''; - $popoverTitle = ''; - - if (\array_key_exists($i, $coverageData)) { - $numTests = ($coverageData[$i] ? \count($coverageData[$i]) : 0); - - if ($coverageData[$i] === null) { - $trClass = ' class="warning"'; - } elseif ($numTests == 0) { - $trClass = ' class="danger"'; - } else { - $lineCss = 'covered-by-large-tests'; - $popoverContent = '
        '; - - if ($numTests > 1) { - $popoverTitle = $numTests . ' tests cover line ' . $i; - } else { - $popoverTitle = '1 test covers line ' . $i; - } - - foreach ($coverageData[$i] as $test) { - if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') { - $lineCss = 'covered-by-medium-tests'; - } elseif ($testData[$test]['size'] == 'small') { - $lineCss = 'covered-by-small-tests'; - } - - switch ($testData[$test]['status']) { - case 0: - switch ($testData[$test]['size']) { - case 'small': - $testCSS = ' class="covered-by-small-tests"'; - - break; - - case 'medium': - $testCSS = ' class="covered-by-medium-tests"'; - - break; - - default: - $testCSS = ' class="covered-by-large-tests"'; - - break; - } - - break; - - case 1: - case 2: - $testCSS = ' class="warning"'; - - break; - - case 3: - $testCSS = ' class="danger"'; - - break; - - case 4: - $testCSS = ' class="danger"'; - - break; - - default: - $testCSS = ''; - } - - $popoverContent .= \sprintf( - '%s', - $testCSS, - \htmlspecialchars($test, $this->htmlSpecialCharsFlags) - ); - } - - $popoverContent .= '
      '; - $trClass = ' class="' . $lineCss . ' popin"'; - } - } - - $popover = ''; - - if (!empty($popoverTitle)) { - $popover = \sprintf( - ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"', - $popoverTitle, - \htmlspecialchars($popoverContent, $this->htmlSpecialCharsFlags) - ); - } - - $lines .= \sprintf( - ' %s' . "\n", - $trClass, - $popover, - $i, - $i, - $i, - $line - ); - - $i++; - } - - return $lines; - } - - /** - * @param string $file - */ - protected function loadFile($file): array - { - $buffer = \file_get_contents($file); - $tokens = \token_get_all($buffer); - $result = ['']; - $i = 0; - $stringFlag = false; - $fileEndsWithNewLine = \substr($buffer, -1) == "\n"; - - unset($buffer); - - foreach ($tokens as $j => $token) { - if (\is_string($token)) { - if ($token === '"' && $tokens[$j - 1] !== '\\') { - $result[$i] .= \sprintf( - '%s', - \htmlspecialchars($token, $this->htmlSpecialCharsFlags) - ); - - $stringFlag = !$stringFlag; - } else { - $result[$i] .= \sprintf( - '%s', - \htmlspecialchars($token, $this->htmlSpecialCharsFlags) - ); - } - - continue; - } - - [$token, $value] = $token; - - $value = \str_replace( - ["\t", ' '], - ['    ', ' '], - \htmlspecialchars($value, $this->htmlSpecialCharsFlags) - ); - - if ($value === "\n") { - $result[++$i] = ''; - } else { - $lines = \explode("\n", $value); - - foreach ($lines as $jj => $line) { - $line = \trim($line); - - if ($line !== '') { - if ($stringFlag) { - $colour = 'string'; - } else { - switch ($token) { - case \T_INLINE_HTML: - $colour = 'html'; - - break; - - case \T_COMMENT: - case \T_DOC_COMMENT: - $colour = 'comment'; - - break; - - case \T_ABSTRACT: - case \T_ARRAY: - case \T_AS: - case \T_BREAK: - case \T_CALLABLE: - case \T_CASE: - case \T_CATCH: - case \T_CLASS: - case \T_CLONE: - case \T_CONTINUE: - case \T_DEFAULT: - case \T_ECHO: - case \T_ELSE: - case \T_ELSEIF: - case \T_EMPTY: - case \T_ENDDECLARE: - case \T_ENDFOR: - case \T_ENDFOREACH: - case \T_ENDIF: - case \T_ENDSWITCH: - case \T_ENDWHILE: - case \T_EXIT: - case \T_EXTENDS: - case \T_FINAL: - case \T_FINALLY: - case \T_FOREACH: - case \T_FUNCTION: - case \T_GLOBAL: - case \T_IF: - case \T_IMPLEMENTS: - case \T_INCLUDE: - case \T_INCLUDE_ONCE: - case \T_INSTANCEOF: - case \T_INSTEADOF: - case \T_INTERFACE: - case \T_ISSET: - case \T_LOGICAL_AND: - case \T_LOGICAL_OR: - case \T_LOGICAL_XOR: - case \T_NAMESPACE: - case \T_NEW: - case \T_PRIVATE: - case \T_PROTECTED: - case \T_PUBLIC: - case \T_REQUIRE: - case \T_REQUIRE_ONCE: - case \T_RETURN: - case \T_STATIC: - case \T_THROW: - case \T_TRAIT: - case \T_TRY: - case \T_UNSET: - case \T_USE: - case \T_VAR: - case \T_WHILE: - case \T_YIELD: - $colour = 'keyword'; - - break; - - default: - $colour = 'default'; - } - } - - $result[$i] .= \sprintf( - '%s', - $colour, - $line - ); - } - - if (isset($lines[$jj + 1])) { - $result[++$i] = ''; - } - } - } - } - - if ($fileEndsWithNewLine) { - unset($result[\count($result) - 1]); - } - - return $result; - } - - private function abbreviateClassName(string $className): string - { - $tmp = \explode('\\', $className); - - if (\count($tmp) > 1) { - $className = \sprintf( - '%s', - $className, - \array_pop($tmp) - ); - } - - return $className; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Generates an HTML report from a code coverage object. - */ -final class Facade -{ - /** - * @var string - */ - private $templatePath; - - /** - * @var string - */ - private $generator; - - /** - * @var int - */ - private $lowUpperBound; - - /** - * @var int - */ - private $highLowerBound; - - public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, string $generator = '') - { - $this->generator = $generator; - $this->highLowerBound = $highLowerBound; - $this->lowUpperBound = $lowUpperBound; - $this->templatePath = __DIR__ . '/Renderer/Template/'; - } - - /** - * @throws RuntimeException - * @throws \InvalidArgumentException - * @throws \RuntimeException - */ - public function process(CodeCoverage $coverage, string $target): void - { - $target = $this->getDirectory($target); - $report = $coverage->getReport(); - - if (!isset($_SERVER['REQUEST_TIME'])) { - $_SERVER['REQUEST_TIME'] = \time(); - } - - $date = \date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']); - - $dashboard = new Dashboard( - $this->templatePath, - $this->generator, - $date, - $this->lowUpperBound, - $this->highLowerBound - ); - - $directory = new Directory( - $this->templatePath, - $this->generator, - $date, - $this->lowUpperBound, - $this->highLowerBound - ); - - $file = new File( - $this->templatePath, - $this->generator, - $date, - $this->lowUpperBound, - $this->highLowerBound - ); - - $directory->render($report, $target . 'index.html'); - $dashboard->render($report, $target . 'dashboard.html'); - - foreach ($report as $node) { - $id = $node->getId(); - - if ($node instanceof DirectoryNode) { - if (!$this->createDirectory($target . $id)) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', $target . $id)); - } - - $directory->render($node, $target . $id . '/index.html'); - $dashboard->render($node, $target . $id . '/dashboard.html'); - } else { - $dir = \dirname($target . $id); - - if (!$this->createDirectory($dir)) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', $dir)); - } - - $file->render($node, $target . $id . '.html'); - } - } - - $this->copyFiles($target); - } - - /** - * @throws RuntimeException - */ - private function copyFiles(string $target): void - { - $dir = $this->getDirectory($target . '.css'); - - \copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css'); - \copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css'); - \copy($this->templatePath . 'css/style.css', $dir . 'style.css'); - \copy($this->templatePath . 'css/custom.css', $dir . 'custom.css'); - \copy($this->templatePath . 'css/octicons.css', $dir . 'octicons.css'); - - $dir = $this->getDirectory($target . '.icons'); - \copy($this->templatePath . 'icons/file-code.svg', $dir . 'file-code.svg'); - \copy($this->templatePath . 'icons/file-directory.svg', $dir . 'file-directory.svg'); - - $dir = $this->getDirectory($target . '.js'); - \copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js'); - \copy($this->templatePath . 'js/popper.min.js', $dir . 'popper.min.js'); - \copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js'); - \copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js'); - \copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js'); - \copy($this->templatePath . 'js/file.js', $dir . 'file.js'); - } - - /** - * @throws RuntimeException - */ - private function getDirectory(string $directory): string - { - if (\substr($directory, -1, 1) != \DIRECTORY_SEPARATOR) { - $directory .= \DIRECTORY_SEPARATOR; - } - - if (!$this->createDirectory($directory)) { - throw new RuntimeException( - \sprintf( - 'Directory "%s" does not exist.', - $directory - ) - ); - } - - return $directory; - } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report\Html; - -use SebastianBergmann\CodeCoverage\Node\AbstractNode; -use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode; -use SebastianBergmann\CodeCoverage\Node\File as FileNode; -use SebastianBergmann\CodeCoverage\Version; -use SebastianBergmann\Environment\Runtime; - -/** - * Base class for node renderers. - */ -abstract class Renderer -{ - /** - * @var string - */ - protected $templatePath; - - /** - * @var string - */ - protected $generator; - - /** - * @var string - */ - protected $date; - - /** - * @var int - */ - protected $lowUpperBound; - - /** - * @var int - */ - protected $highLowerBound; - - /** - * @var string - */ - protected $version; - - public function __construct(string $templatePath, string $generator, string $date, int $lowUpperBound, int $highLowerBound) - { - $this->templatePath = $templatePath; - $this->generator = $generator; - $this->date = $date; - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - $this->version = Version::id(); - } - - protected function renderItemTemplate(\Text_Template $template, array $data): string - { - $numSeparator = ' / '; - - if (isset($data['numClasses']) && $data['numClasses'] > 0) { - $classesLevel = $this->getColorLevel($data['testedClassesPercent']); - - $classesNumber = $data['numTestedClasses'] . $numSeparator . - $data['numClasses']; - - $classesBar = $this->getCoverageBar( - $data['testedClassesPercent'] - ); - } else { - $classesLevel = ''; - $classesNumber = '0' . $numSeparator . '0'; - $classesBar = ''; - $data['testedClassesPercentAsString'] = 'n/a'; - } - - if ($data['numMethods'] > 0) { - $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']); - - $methodsNumber = $data['numTestedMethods'] . $numSeparator . - $data['numMethods']; - - $methodsBar = $this->getCoverageBar( - $data['testedMethodsPercent'] - ); - } else { - $methodsLevel = ''; - $methodsNumber = '0' . $numSeparator . '0'; - $methodsBar = ''; - $data['testedMethodsPercentAsString'] = 'n/a'; - } - - if ($data['numExecutableLines'] > 0) { - $linesLevel = $this->getColorLevel($data['linesExecutedPercent']); - - $linesNumber = $data['numExecutedLines'] . $numSeparator . - $data['numExecutableLines']; - - $linesBar = $this->getCoverageBar( - $data['linesExecutedPercent'] - ); - } else { - $linesLevel = ''; - $linesNumber = '0' . $numSeparator . '0'; - $linesBar = ''; - $data['linesExecutedPercentAsString'] = 'n/a'; - } - - $template->setVar( - [ - 'icon' => $data['icon'] ?? '', - 'crap' => $data['crap'] ?? '', - 'name' => $data['name'], - 'lines_bar' => $linesBar, - 'lines_executed_percent' => $data['linesExecutedPercentAsString'], - 'lines_level' => $linesLevel, - 'lines_number' => $linesNumber, - 'methods_bar' => $methodsBar, - 'methods_tested_percent' => $data['testedMethodsPercentAsString'], - 'methods_level' => $methodsLevel, - 'methods_number' => $methodsNumber, - 'classes_bar' => $classesBar, - 'classes_tested_percent' => $data['testedClassesPercentAsString'] ?? '', - 'classes_level' => $classesLevel, - 'classes_number' => $classesNumber, - ] - ); - - return $template->render(); - } - - protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node): void - { - $template->setVar( - [ - 'id' => $node->getId(), - 'full_path' => $node->getPath(), - 'path_to_root' => $this->getPathToRoot($node), - 'breadcrumbs' => $this->getBreadcrumbs($node), - 'date' => $this->date, - 'version' => $this->version, - 'runtime' => $this->getRuntimeString(), - 'generator' => $this->generator, - 'low_upper_bound' => $this->lowUpperBound, - 'high_lower_bound' => $this->highLowerBound, - ] - ); - } - - protected function getBreadcrumbs(AbstractNode $node): string - { - $breadcrumbs = ''; - $path = $node->getPathAsArray(); - $pathToRoot = []; - $max = \count($path); - - if ($node instanceof FileNode) { - $max--; - } - - for ($i = 0; $i < $max; $i++) { - $pathToRoot[] = \str_repeat('../', $i); - } - - foreach ($path as $step) { - if ($step !== $node) { - $breadcrumbs .= $this->getInactiveBreadcrumb( - $step, - \array_pop($pathToRoot) - ); - } else { - $breadcrumbs .= $this->getActiveBreadcrumb($step); - } - } - - return $breadcrumbs; - } - - protected function getActiveBreadcrumb(AbstractNode $node): string - { - $buffer = \sprintf( - ' ' . "\n", - $node->getName() - ); - - if ($node instanceof DirectoryNode) { - $buffer .= ' ' . "\n"; - } - - return $buffer; - } - - protected function getInactiveBreadcrumb(AbstractNode $node, string $pathToRoot): string - { - return \sprintf( - ' ' . "\n", - $pathToRoot, - $node->getName() - ); - } - - protected function getPathToRoot(AbstractNode $node): string - { - $id = $node->getId(); - $depth = \substr_count($id, '/'); - - if ($id !== 'index' && - $node instanceof DirectoryNode) { - $depth++; - } - - return \str_repeat('../', $depth); - } - - protected function getCoverageBar(float $percent): string - { - $level = $this->getColorLevel($percent); - - $template = new \Text_Template( - $this->templatePath . 'coverage_bar.html', - '{{', - '}}' - ); - - $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]); - - return $template->render(); - } - - protected function getColorLevel(float $percent): string - { - if ($percent <= $this->lowUpperBound) { - return 'danger'; - } - - if ($percent > $this->lowUpperBound && - $percent < $this->highLowerBound) { - return 'warning'; - } - - return 'success'; - } - - private function getRuntimeString(): string - { - $runtime = new Runtime; - - $buffer = \sprintf( - '%s %s', - $runtime->getVendorUrl(), - $runtime->getName(), - $runtime->getVersion() - ); - - if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) { - $buffer .= \sprintf( - ' with Xdebug %s', - \phpversion('xdebug') - ); - } - - return $buffer; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Node\File; -use SebastianBergmann\CodeCoverage\Util; - -/** - * Generates human readable output from a code coverage object. - * - * The output gets put into a text file our written to the CLI. - */ -final class Text -{ - /** - * @var string - */ - private const COLOR_GREEN = "\x1b[30;42m"; - - /** - * @var string - */ - private const COLOR_YELLOW = "\x1b[30;43m"; - - /** - * @var string - */ - private const COLOR_RED = "\x1b[37;41m"; - - /** - * @var string - */ - private const COLOR_HEADER = "\x1b[1;37;40m"; - - /** - * @var string - */ - private const COLOR_RESET = "\x1b[0m"; - - /** - * @var string - */ - private const COLOR_EOL = "\x1b[2K"; - - /** - * @var int - */ - private $lowUpperBound; - - /** - * @var int - */ - private $highLowerBound; - - /** - * @var bool - */ - private $showUncoveredFiles; - - /** - * @var bool - */ - private $showOnlySummary; - - public function __construct(int $lowUpperBound = 50, int $highLowerBound = 90, bool $showUncoveredFiles = false, bool $showOnlySummary = false) - { - $this->lowUpperBound = $lowUpperBound; - $this->highLowerBound = $highLowerBound; - $this->showUncoveredFiles = $showUncoveredFiles; - $this->showOnlySummary = $showOnlySummary; - } - - public function process(CodeCoverage $coverage, bool $showColors = false): string - { - $output = \PHP_EOL . \PHP_EOL; - $report = $coverage->getReport(); - - $colors = [ - 'header' => '', - 'classes' => '', - 'methods' => '', - 'lines' => '', - 'reset' => '', - 'eol' => '', - ]; - - if ($showColors) { - $colors['classes'] = $this->getCoverageColor( - $report->getNumTestedClassesAndTraits(), - $report->getNumClassesAndTraits() - ); - - $colors['methods'] = $this->getCoverageColor( - $report->getNumTestedMethods(), - $report->getNumMethods() - ); - - $colors['lines'] = $this->getCoverageColor( - $report->getNumExecutedLines(), - $report->getNumExecutableLines() - ); - - $colors['reset'] = self::COLOR_RESET; - $colors['header'] = self::COLOR_HEADER; - $colors['eol'] = self::COLOR_EOL; - } - - $classes = \sprintf( - ' Classes: %6s (%d/%d)', - Util::percent( - $report->getNumTestedClassesAndTraits(), - $report->getNumClassesAndTraits(), - true - ), - $report->getNumTestedClassesAndTraits(), - $report->getNumClassesAndTraits() - ); - - $methods = \sprintf( - ' Methods: %6s (%d/%d)', - Util::percent( - $report->getNumTestedMethods(), - $report->getNumMethods(), - true - ), - $report->getNumTestedMethods(), - $report->getNumMethods() - ); - - $lines = \sprintf( - ' Lines: %6s (%d/%d)', - Util::percent( - $report->getNumExecutedLines(), - $report->getNumExecutableLines(), - true - ), - $report->getNumExecutedLines(), - $report->getNumExecutableLines() - ); - - $padding = \max(\array_map('strlen', [$classes, $methods, $lines])); - - if ($this->showOnlySummary) { - $title = 'Code Coverage Report Summary:'; - $padding = \max($padding, \strlen($title)); - - $output .= $this->format($colors['header'], $padding, $title); - } else { - $date = \date(' Y-m-d H:i:s', $_SERVER['REQUEST_TIME']); - $title = 'Code Coverage Report:'; - - $output .= $this->format($colors['header'], $padding, $title); - $output .= $this->format($colors['header'], $padding, $date); - $output .= $this->format($colors['header'], $padding, ''); - $output .= $this->format($colors['header'], $padding, ' Summary:'); - } - - $output .= $this->format($colors['classes'], $padding, $classes); - $output .= $this->format($colors['methods'], $padding, $methods); - $output .= $this->format($colors['lines'], $padding, $lines); - - if ($this->showOnlySummary) { - return $output . \PHP_EOL; - } - - $classCoverage = []; - - foreach ($report as $item) { - if (!$item instanceof File) { - continue; - } - - $classes = $item->getClassesAndTraits(); - - foreach ($classes as $className => $class) { - $classStatements = 0; - $coveredClassStatements = 0; - $coveredMethods = 0; - $classMethods = 0; - - foreach ($class['methods'] as $method) { - if ($method['executableLines'] == 0) { - continue; - } - - $classMethods++; - $classStatements += $method['executableLines']; - $coveredClassStatements += $method['executedLines']; - - if ($method['coverage'] == 100) { - $coveredMethods++; - } - } - - $namespace = ''; - - if (!empty($class['package']['namespace'])) { - $namespace = '\\' . $class['package']['namespace'] . '::'; - } elseif (!empty($class['package']['fullPackage'])) { - $namespace = '@' . $class['package']['fullPackage'] . '::'; - } - - $classCoverage[$namespace . $className] = [ - 'namespace' => $namespace, - 'className ' => $className, - 'methodsCovered' => $coveredMethods, - 'methodCount' => $classMethods, - 'statementsCovered' => $coveredClassStatements, - 'statementCount' => $classStatements, - ]; - } - } - - \ksort($classCoverage); - - $methodColor = ''; - $linesColor = ''; - $resetColor = ''; - - foreach ($classCoverage as $fullQualifiedPath => $classInfo) { - if ($this->showUncoveredFiles || $classInfo['statementsCovered'] != 0) { - if ($showColors) { - $methodColor = $this->getCoverageColor($classInfo['methodsCovered'], $classInfo['methodCount']); - $linesColor = $this->getCoverageColor($classInfo['statementsCovered'], $classInfo['statementCount']); - $resetColor = $colors['reset']; - } - - $output .= \PHP_EOL . $fullQualifiedPath . \PHP_EOL - . ' ' . $methodColor . 'Methods: ' . $this->printCoverageCounts($classInfo['methodsCovered'], $classInfo['methodCount'], 2) . $resetColor . ' ' - . ' ' . $linesColor . 'Lines: ' . $this->printCoverageCounts($classInfo['statementsCovered'], $classInfo['statementCount'], 3) . $resetColor; - } - } - - return $output . \PHP_EOL; - } - - private function getCoverageColor(int $numberOfCoveredElements, int $totalNumberOfElements): string - { - $coverage = Util::percent( - $numberOfCoveredElements, - $totalNumberOfElements - ); - - if ($coverage >= $this->highLowerBound) { - return self::COLOR_GREEN; - } - - if ($coverage > $this->lowUpperBound) { - return self::COLOR_YELLOW; - } - - return self::COLOR_RED; - } - - private function printCoverageCounts(int $numberOfCoveredElements, int $totalNumberOfElements, int $precision): string - { - $format = '%' . $precision . 's'; - - return Util::percent( - $numberOfCoveredElements, - $totalNumberOfElements, - true, - true - ) . - ' (' . \sprintf($format, $numberOfCoveredElements) . '/' . - \sprintf($format, $totalNumberOfElements) . ')'; - } - - private function format($color, $padding, $string): string - { - $reset = $color ? self::COLOR_RESET : ''; - - return $color . \str_pad($string, $padding) . $reset . \PHP_EOL; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Report; - -use SebastianBergmann\CodeCoverage\CodeCoverage; -use SebastianBergmann\CodeCoverage\Node\File; -use SebastianBergmann\CodeCoverage\RuntimeException; - -final class Crap4j -{ - /** - * @var int - */ - private $threshold; - - public function __construct(int $threshold = 30) - { - $this->threshold = $threshold; - } - - /** - * @throws \RuntimeException - */ - public function process(CodeCoverage $coverage, ?string $target = null, ?string $name = null): string - { - $document = new \DOMDocument('1.0', 'UTF-8'); - $document->formatOutput = true; - - $root = $document->createElement('crap_result'); - $document->appendChild($root); - - $project = $document->createElement('project', \is_string($name) ? $name : ''); - $root->appendChild($project); - $root->appendChild($document->createElement('timestamp', \date('Y-m-d H:i:s', (int) $_SERVER['REQUEST_TIME']))); - - $stats = $document->createElement('stats'); - $methodsNode = $document->createElement('methods'); - - $report = $coverage->getReport(); - unset($coverage); - - $fullMethodCount = 0; - $fullCrapMethodCount = 0; - $fullCrapLoad = 0; - $fullCrap = 0; - - foreach ($report as $item) { - $namespace = 'global'; - - if (!$item instanceof File) { - continue; - } - - $file = $document->createElement('file'); - $file->setAttribute('name', $item->getPath()); - - $classes = $item->getClassesAndTraits(); - - foreach ($classes as $className => $class) { - foreach ($class['methods'] as $methodName => $method) { - $crapLoad = $this->getCrapLoad($method['crap'], $method['ccn'], $method['coverage']); - - $fullCrap += $method['crap']; - $fullCrapLoad += $crapLoad; - $fullMethodCount++; - - if ($method['crap'] >= $this->threshold) { - $fullCrapMethodCount++; - } - - $methodNode = $document->createElement('method'); - - if (!empty($class['package']['namespace'])) { - $namespace = $class['package']['namespace']; - } - - $methodNode->appendChild($document->createElement('package', $namespace)); - $methodNode->appendChild($document->createElement('className', $className)); - $methodNode->appendChild($document->createElement('methodName', $methodName)); - $methodNode->appendChild($document->createElement('methodSignature', \htmlspecialchars($method['signature']))); - $methodNode->appendChild($document->createElement('fullMethod', \htmlspecialchars($method['signature']))); - $methodNode->appendChild($document->createElement('crap', $this->roundValue($method['crap']))); - $methodNode->appendChild($document->createElement('complexity', $method['ccn'])); - $methodNode->appendChild($document->createElement('coverage', $this->roundValue($method['coverage']))); - $methodNode->appendChild($document->createElement('crapLoad', \round($crapLoad))); - - $methodsNode->appendChild($methodNode); - } - } - } - - $stats->appendChild($document->createElement('name', 'Method Crap Stats')); - $stats->appendChild($document->createElement('methodCount', $fullMethodCount)); - $stats->appendChild($document->createElement('crapMethodCount', $fullCrapMethodCount)); - $stats->appendChild($document->createElement('crapLoad', \round($fullCrapLoad))); - $stats->appendChild($document->createElement('totalCrap', $fullCrap)); - - $crapMethodPercent = 0; - - if ($fullMethodCount > 0) { - $crapMethodPercent = $this->roundValue((100 * $fullCrapMethodCount) / $fullMethodCount); - } - - $stats->appendChild($document->createElement('crapMethodPercent', $crapMethodPercent)); - - $root->appendChild($stats); - $root->appendChild($methodsNode); - - $buffer = $document->saveXML(); - - if ($target !== null) { - if (!$this->createDirectory(\dirname($target))) { - throw new \RuntimeException(\sprintf('Directory "%s" was not created', \dirname($target))); - } - - if (@\file_put_contents($target, $buffer) === false) { - throw new RuntimeException( - \sprintf( - 'Could not write to "%s', - $target - ) - ); - } - } - - return $buffer; - } - - /** - * @param float $crapValue - * @param int $cyclomaticComplexity - * @param float $coveragePercent - */ - private function getCrapLoad($crapValue, $cyclomaticComplexity, $coveragePercent): float - { - $crapLoad = 0; - - if ($crapValue >= $this->threshold) { - $crapLoad += $cyclomaticComplexity * (1.0 - $coveragePercent / 100); - $crapLoad += $cyclomaticComplexity / $this->threshold; - } - - return $crapLoad; - } - - /** - * @param float $value - */ - private function roundValue($value): float - { - return \round($value, 2); - } - - private function createDirectory(string $directory): bool - { - return !(!\is_dir($directory) && !@\mkdir($directory, 0777, true) && !\is_dir($directory)); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -use PHPUnit\Framework\TestCase; -use PHPUnit\Runner\PhptTestCase; -use PHPUnit\Util\Test; -use SebastianBergmann\CodeCoverage\Driver\Driver; -use SebastianBergmann\CodeCoverage\Driver\PHPDBG; -use SebastianBergmann\CodeCoverage\Driver\Xdebug; -use SebastianBergmann\CodeCoverage\Node\Builder; -use SebastianBergmann\CodeCoverage\Node\Directory; -use SebastianBergmann\CodeUnitReverseLookup\Wizard; -use SebastianBergmann\Environment\Runtime; - -/** - * Provides collection functionality for PHP code coverage information. - */ -final class CodeCoverage -{ - /** - * @var Driver - */ - private $driver; - - /** - * @var Filter - */ - private $filter; - - /** - * @var Wizard - */ - private $wizard; - - /** - * @var bool - */ - private $cacheTokens = false; - - /** - * @var bool - */ - private $checkForUnintentionallyCoveredCode = false; - - /** - * @var bool - */ - private $forceCoversAnnotation = false; - - /** - * @var bool - */ - private $checkForUnexecutedCoveredCode = false; - - /** - * @var bool - */ - private $checkForMissingCoversAnnotation = false; - - /** - * @var bool - */ - private $addUncoveredFilesFromWhitelist = true; - - /** - * @var bool - */ - private $processUncoveredFilesFromWhitelist = false; - - /** - * @var bool - */ - private $ignoreDeprecatedCode = false; - - /** - * @var PhptTestCase|string|TestCase - */ - private $currentId; - - /** - * Code coverage data. - * - * @var array - */ - private $data = []; - - /** - * @var array - */ - private $ignoredLines = []; - - /** - * @var bool - */ - private $disableIgnoredLines = false; - - /** - * Test data. - * - * @var array - */ - private $tests = []; - - /** - * @var string[] - */ - private $unintentionallyCoveredSubclassesWhitelist = []; - - /** - * Determine if the data has been initialized or not - * - * @var bool - */ - private $isInitialized = false; - - /** - * Determine whether we need to check for dead and unused code on each test - * - * @var bool - */ - private $shouldCheckForDeadAndUnused = true; - - /** - * @var Directory - */ - private $report; - - /** - * @throws RuntimeException - */ - public function __construct(Driver $driver = null, Filter $filter = null) - { - if ($filter === null) { - $filter = new Filter; - } - - if ($driver === null) { - $driver = $this->selectDriver($filter); - } - - $this->driver = $driver; - $this->filter = $filter; - - $this->wizard = new Wizard; - } - - /** - * Returns the code coverage information as a graph of node objects. - */ - public function getReport(): Directory - { - if ($this->report === null) { - $builder = new Builder; - - $this->report = $builder->build($this); - } - - return $this->report; - } - - /** - * Clears collected code coverage data. - */ - public function clear(): void - { - $this->isInitialized = false; - $this->currentId = null; - $this->data = []; - $this->tests = []; - $this->report = null; - } - - /** - * Returns the filter object used. - */ - public function filter(): Filter - { - return $this->filter; - } - - /** - * Returns the collected code coverage data. - */ - public function getData(bool $raw = false): array - { - if (!$raw && $this->addUncoveredFilesFromWhitelist) { - $this->addUncoveredFilesFromWhitelist(); - } - - return $this->data; - } - - /** - * Sets the coverage data. - */ - public function setData(array $data): void - { - $this->data = $data; - $this->report = null; - } - - /** - * Returns the test data. - */ - public function getTests(): array - { - return $this->tests; - } - - /** - * Sets the test data. - */ - public function setTests(array $tests): void - { - $this->tests = $tests; - } - - /** - * Start collection of code coverage information. - * - * @param PhptTestCase|string|TestCase $id - * - * @throws RuntimeException - */ - public function start($id, bool $clear = false): void - { - if ($clear) { - $this->clear(); - } - - if ($this->isInitialized === false) { - $this->initializeData(); - } - - $this->currentId = $id; - - $this->driver->start($this->shouldCheckForDeadAndUnused); - } - - /** - * Stop collection of code coverage information. - * - * @param array|false $linesToBeCovered - * - * @throws MissingCoversAnnotationException - * @throws CoveredCodeNotExecutedException - * @throws RuntimeException - * @throws InvalidArgumentException - * @throws \ReflectionException - */ - public function stop(bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): array - { - if (!\is_array($linesToBeCovered) && $linesToBeCovered !== false) { - throw InvalidArgumentException::create( - 2, - 'array or false' - ); - } - - $data = $this->driver->stop(); - $this->append($data, null, $append, $linesToBeCovered, $linesToBeUsed, $ignoreForceCoversAnnotation); - - $this->currentId = null; - - return $data; - } - - /** - * Appends code coverage data. - * - * @param PhptTestCase|string|TestCase $id - * @param array|false $linesToBeCovered - * - * @throws \SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException - * @throws \SebastianBergmann\CodeCoverage\MissingCoversAnnotationException - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \ReflectionException - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - * @throws RuntimeException - */ - public function append(array $data, $id = null, bool $append = true, $linesToBeCovered = [], array $linesToBeUsed = [], bool $ignoreForceCoversAnnotation = false): void - { - if ($id === null) { - $id = $this->currentId; - } - - if ($id === null) { - throw new RuntimeException; - } - - $this->applyWhitelistFilter($data); - $this->applyIgnoredLinesFilter($data); - $this->initializeFilesThatAreSeenTheFirstTime($data); - - if (!$append) { - return; - } - - if ($id !== 'UNCOVERED_FILES_FROM_WHITELIST') { - $this->applyCoversAnnotationFilter( - $data, - $linesToBeCovered, - $linesToBeUsed, - $ignoreForceCoversAnnotation - ); - } - - if (empty($data)) { - return; - } - - $size = 'unknown'; - $status = -1; - - if ($id instanceof TestCase) { - $_size = $id->getSize(); - - if ($_size === Test::SMALL) { - $size = 'small'; - } elseif ($_size === Test::MEDIUM) { - $size = 'medium'; - } elseif ($_size === Test::LARGE) { - $size = 'large'; - } - - $status = $id->getStatus(); - $id = \get_class($id) . '::' . $id->getName(); - } elseif ($id instanceof PhptTestCase) { - $size = 'large'; - $id = $id->getName(); - } - - $this->tests[$id] = ['size' => $size, 'status' => $status]; - - foreach ($data as $file => $lines) { - if (!$this->filter->isFile($file)) { - continue; - } - - foreach ($lines as $k => $v) { - if ($v === Driver::LINE_EXECUTED) { - if (empty($this->data[$file][$k]) || !\in_array($id, $this->data[$file][$k])) { - $this->data[$file][$k][] = $id; - } - } - } - } - - $this->report = null; - } - - /** - * Merges the data from another instance. - * - * @param CodeCoverage $that - */ - public function merge(self $that): void - { - $this->filter->setWhitelistedFiles( - \array_merge($this->filter->getWhitelistedFiles(), $that->filter()->getWhitelistedFiles()) - ); - - foreach ($that->data as $file => $lines) { - if (!isset($this->data[$file])) { - if (!$this->filter->isFiltered($file)) { - $this->data[$file] = $lines; - } - - continue; - } - - // we should compare the lines if any of two contains data - $compareLineNumbers = \array_unique( - \array_merge( - \array_keys($this->data[$file]), - \array_keys($that->data[$file]) - ) - ); - - foreach ($compareLineNumbers as $line) { - $thatPriority = $this->getLinePriority($that->data[$file], $line); - $thisPriority = $this->getLinePriority($this->data[$file], $line); - - if ($thatPriority > $thisPriority) { - $this->data[$file][$line] = $that->data[$file][$line]; - } elseif ($thatPriority === $thisPriority && \is_array($this->data[$file][$line])) { - $this->data[$file][$line] = \array_unique( - \array_merge($this->data[$file][$line], $that->data[$file][$line]) - ); - } - } - } - - $this->tests = \array_merge($this->tests, $that->getTests()); - $this->report = null; - } - - public function setCacheTokens(bool $flag): void - { - $this->cacheTokens = $flag; - } - - public function getCacheTokens(): bool - { - return $this->cacheTokens; - } - - public function setCheckForUnintentionallyCoveredCode(bool $flag): void - { - $this->checkForUnintentionallyCoveredCode = $flag; - } - - public function setForceCoversAnnotation(bool $flag): void - { - $this->forceCoversAnnotation = $flag; - } - - public function setCheckForMissingCoversAnnotation(bool $flag): void - { - $this->checkForMissingCoversAnnotation = $flag; - } - - public function setCheckForUnexecutedCoveredCode(bool $flag): void - { - $this->checkForUnexecutedCoveredCode = $flag; - } - - public function setAddUncoveredFilesFromWhitelist(bool $flag): void - { - $this->addUncoveredFilesFromWhitelist = $flag; - } - - public function setProcessUncoveredFilesFromWhitelist(bool $flag): void - { - $this->processUncoveredFilesFromWhitelist = $flag; - } - - public function setDisableIgnoredLines(bool $flag): void - { - $this->disableIgnoredLines = $flag; - } - - public function setIgnoreDeprecatedCode(bool $flag): void - { - $this->ignoreDeprecatedCode = $flag; - } - - public function setUnintentionallyCoveredSubclassesWhitelist(array $whitelist): void - { - $this->unintentionallyCoveredSubclassesWhitelist = $whitelist; - } - - /** - * Determine the priority for a line - * - * 1 = the line is not set - * 2 = the line has not been tested - * 3 = the line is dead code - * 4 = the line has been tested - * - * During a merge, a higher number is better. - * - * @param array $data - * @param int $line - * - * @return int - */ - private function getLinePriority($data, $line) - { - if (!\array_key_exists($line, $data)) { - return 1; - } - - if (\is_array($data[$line]) && \count($data[$line]) === 0) { - return 2; - } - - if ($data[$line] === null) { - return 3; - } - - return 4; - } - - /** - * Applies the @covers annotation filtering. - * - * @param array|false $linesToBeCovered - * - * @throws \SebastianBergmann\CodeCoverage\CoveredCodeNotExecutedException - * @throws \ReflectionException - * @throws MissingCoversAnnotationException - * @throws UnintentionallyCoveredCodeException - */ - private function applyCoversAnnotationFilter(array &$data, $linesToBeCovered, array $linesToBeUsed, bool $ignoreForceCoversAnnotation): void - { - if ($linesToBeCovered === false || - ($this->forceCoversAnnotation && empty($linesToBeCovered) && !$ignoreForceCoversAnnotation)) { - if ($this->checkForMissingCoversAnnotation) { - throw new MissingCoversAnnotationException; - } - - $data = []; - - return; - } - - if (empty($linesToBeCovered)) { - return; - } - - if ($this->checkForUnintentionallyCoveredCode && - (!$this->currentId instanceof TestCase || - (!$this->currentId->isMedium() && !$this->currentId->isLarge()))) { - $this->performUnintentionallyCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); - } - - if ($this->checkForUnexecutedCoveredCode) { - $this->performUnexecutedCoveredCodeCheck($data, $linesToBeCovered, $linesToBeUsed); - } - - $data = \array_intersect_key($data, $linesToBeCovered); - - foreach (\array_keys($data) as $filename) { - $_linesToBeCovered = \array_flip($linesToBeCovered[$filename]); - $data[$filename] = \array_intersect_key($data[$filename], $_linesToBeCovered); - } - } - - private function applyWhitelistFilter(array &$data): void - { - foreach (\array_keys($data) as $filename) { - if ($this->filter->isFiltered($filename)) { - unset($data[$filename]); - } - } - } - - /** - * @throws \SebastianBergmann\CodeCoverage\InvalidArgumentException - */ - private function applyIgnoredLinesFilter(array &$data): void - { - foreach (\array_keys($data) as $filename) { - if (!$this->filter->isFile($filename)) { - continue; - } - - foreach ($this->getLinesToBeIgnored($filename) as $line) { - unset($data[$filename][$line]); - } - } - } - - private function initializeFilesThatAreSeenTheFirstTime(array $data): void - { - foreach ($data as $file => $lines) { - if (!isset($this->data[$file]) && $this->filter->isFile($file)) { - $this->data[$file] = []; - - foreach ($lines as $k => $v) { - $this->data[$file][$k] = $v === -2 ? null : []; - } - } - } - } - - /** - * @throws CoveredCodeNotExecutedException - * @throws InvalidArgumentException - * @throws MissingCoversAnnotationException - * @throws RuntimeException - * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException - */ - private function addUncoveredFilesFromWhitelist(): void - { - $data = []; - $uncoveredFiles = \array_diff( - $this->filter->getWhitelist(), - \array_keys($this->data) - ); - - foreach ($uncoveredFiles as $uncoveredFile) { - if (!\file_exists($uncoveredFile)) { - continue; - } - - $data[$uncoveredFile] = []; - - $lines = \count(\file($uncoveredFile)); - - for ($i = 1; $i <= $lines; $i++) { - $data[$uncoveredFile][$i] = Driver::LINE_NOT_EXECUTED; - } - } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); - } - - private function getLinesToBeIgnored(string $fileName): array - { - if (isset($this->ignoredLines[$fileName])) { - return $this->ignoredLines[$fileName]; - } - - try { - return $this->getLinesToBeIgnoredInner($fileName); - } catch (\OutOfBoundsException $e) { - // This can happen with PHP_Token_Stream if the file is syntactically invalid, - // and probably affects a file that wasn't executed. - return []; - } - } - - private function getLinesToBeIgnoredInner(string $fileName): array - { - $this->ignoredLines[$fileName] = []; - - $lines = \file($fileName); - - foreach ($lines as $index => $line) { - if (!\trim($line)) { - $this->ignoredLines[$fileName][] = $index + 1; - } - } - - if ($this->cacheTokens) { - $tokens = \PHP_Token_Stream_CachingFactory::get($fileName); - } else { - $tokens = new \PHP_Token_Stream($fileName); - } - - foreach ($tokens->getInterfaces() as $interface) { - $interfaceStartLine = $interface['startLine']; - $interfaceEndLine = $interface['endLine']; - - foreach (\range($interfaceStartLine, $interfaceEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - } - - foreach (\array_merge($tokens->getClasses(), $tokens->getTraits()) as $classOrTrait) { - $classOrTraitStartLine = $classOrTrait['startLine']; - $classOrTraitEndLine = $classOrTrait['endLine']; - - if (empty($classOrTrait['methods'])) { - foreach (\range($classOrTraitStartLine, $classOrTraitEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - - continue; - } - - $firstMethod = \array_shift($classOrTrait['methods']); - $firstMethodStartLine = $firstMethod['startLine']; - $firstMethodEndLine = $firstMethod['endLine']; - $lastMethodEndLine = $firstMethodEndLine; - - do { - $lastMethod = \array_pop($classOrTrait['methods']); - } while ($lastMethod !== null && 0 === \strpos($lastMethod['signature'], 'anonymousFunction')); - - if ($lastMethod !== null) { - $lastMethodEndLine = $lastMethod['endLine']; - } - - foreach (\range($classOrTraitStartLine, $firstMethodStartLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - - foreach (\range($lastMethodEndLine + 1, $classOrTraitEndLine) as $line) { - $this->ignoredLines[$fileName][] = $line; - } - } - - if ($this->disableIgnoredLines) { - $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); - \sort($this->ignoredLines[$fileName]); - - return $this->ignoredLines[$fileName]; - } - - $ignore = false; - $stop = false; - - foreach ($tokens->tokens() as $token) { - switch (\get_class($token)) { - case \PHP_Token_COMMENT::class: - case \PHP_Token_DOC_COMMENT::class: - $_token = \trim($token); - $_line = \trim($lines[$token->getLine() - 1]); - - if ($_token === '// @codeCoverageIgnore' || - $_token === '//@codeCoverageIgnore') { - $ignore = true; - $stop = true; - } elseif ($_token === '// @codeCoverageIgnoreStart' || - $_token === '//@codeCoverageIgnoreStart') { - $ignore = true; - } elseif ($_token === '// @codeCoverageIgnoreEnd' || - $_token === '//@codeCoverageIgnoreEnd') { - $stop = true; - } - - if (!$ignore) { - $start = $token->getLine(); - $end = $start + \substr_count($token, "\n"); - - // Do not ignore the first line when there is a token - // before the comment - if (0 !== \strpos($_token, $_line)) { - $start++; - } - - for ($i = $start; $i < $end; $i++) { - $this->ignoredLines[$fileName][] = $i; - } - - // A DOC_COMMENT token or a COMMENT token starting with "/*" - // does not contain the final \n character in its text - if (isset($lines[$i - 1]) && 0 === \strpos($_token, '/*') && '*/' === \substr(\trim($lines[$i - 1]), -2)) { - $this->ignoredLines[$fileName][] = $i; - } - } - - break; - - case \PHP_Token_INTERFACE::class: - case \PHP_Token_TRAIT::class: - case \PHP_Token_CLASS::class: - case \PHP_Token_FUNCTION::class: - /* @var \PHP_Token_Interface $token */ - - $docblock = $token->getDocblock(); - - $this->ignoredLines[$fileName][] = $token->getLine(); - - if (\strpos($docblock, '@codeCoverageIgnore') || ($this->ignoreDeprecatedCode && \strpos($docblock, '@deprecated'))) { - $endLine = $token->getEndLine(); - - for ($i = $token->getLine(); $i <= $endLine; $i++) { - $this->ignoredLines[$fileName][] = $i; - } - } - - break; - - /* @noinspection PhpMissingBreakStatementInspection */ - case \PHP_Token_NAMESPACE::class: - $this->ignoredLines[$fileName][] = $token->getEndLine(); - - // Intentional fallthrough - case \PHP_Token_DECLARE::class: - case \PHP_Token_OPEN_TAG::class: - case \PHP_Token_CLOSE_TAG::class: - case \PHP_Token_USE::class: - $this->ignoredLines[$fileName][] = $token->getLine(); - - break; - } - - if ($ignore) { - $this->ignoredLines[$fileName][] = $token->getLine(); - - if ($stop) { - $ignore = false; - $stop = false; - } - } - } - - $this->ignoredLines[$fileName][] = \count($lines) + 1; - - $this->ignoredLines[$fileName] = \array_unique( - $this->ignoredLines[$fileName] - ); - - $this->ignoredLines[$fileName] = \array_unique($this->ignoredLines[$fileName]); - \sort($this->ignoredLines[$fileName]); - - return $this->ignoredLines[$fileName]; - } - - /** - * @throws \ReflectionException - * @throws UnintentionallyCoveredCodeException - */ - private function performUnintentionallyCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void - { - $allowedLines = $this->getAllowedLines( - $linesToBeCovered, - $linesToBeUsed - ); - - $unintentionallyCoveredUnits = []; - - foreach ($data as $file => $_data) { - foreach ($_data as $line => $flag) { - if ($flag === 1 && !isset($allowedLines[$file][$line])) { - $unintentionallyCoveredUnits[] = $this->wizard->lookup($file, $line); - } - } - } - - $unintentionallyCoveredUnits = $this->processUnintentionallyCoveredUnits($unintentionallyCoveredUnits); - - if (!empty($unintentionallyCoveredUnits)) { - throw new UnintentionallyCoveredCodeException( - $unintentionallyCoveredUnits - ); - } - } - - /** - * @throws CoveredCodeNotExecutedException - */ - private function performUnexecutedCoveredCodeCheck(array &$data, array $linesToBeCovered, array $linesToBeUsed): void - { - $executedCodeUnits = $this->coverageToCodeUnits($data); - $message = ''; - - foreach ($this->linesToCodeUnits($linesToBeCovered) as $codeUnit) { - if (!\in_array($codeUnit, $executedCodeUnits)) { - $message .= \sprintf( - '- %s is expected to be executed (@covers) but was not executed' . "\n", - $codeUnit - ); - } - } - - foreach ($this->linesToCodeUnits($linesToBeUsed) as $codeUnit) { - if (!\in_array($codeUnit, $executedCodeUnits)) { - $message .= \sprintf( - '- %s is expected to be executed (@uses) but was not executed' . "\n", - $codeUnit - ); - } - } - - if (!empty($message)) { - throw new CoveredCodeNotExecutedException($message); - } - } - - private function getAllowedLines(array $linesToBeCovered, array $linesToBeUsed): array - { - $allowedLines = []; - - foreach (\array_keys($linesToBeCovered) as $file) { - if (!isset($allowedLines[$file])) { - $allowedLines[$file] = []; - } - - $allowedLines[$file] = \array_merge( - $allowedLines[$file], - $linesToBeCovered[$file] - ); - } - - foreach (\array_keys($linesToBeUsed) as $file) { - if (!isset($allowedLines[$file])) { - $allowedLines[$file] = []; - } - - $allowedLines[$file] = \array_merge( - $allowedLines[$file], - $linesToBeUsed[$file] - ); - } - - foreach (\array_keys($allowedLines) as $file) { - $allowedLines[$file] = \array_flip( - \array_unique($allowedLines[$file]) - ); - } - - return $allowedLines; - } - - /** - * @throws RuntimeException - */ - private function selectDriver(Filter $filter): Driver - { - $runtime = new Runtime; - - if (!$runtime->canCollectCodeCoverage()) { - throw new RuntimeException('No code coverage driver available'); - } - - if ($runtime->isPHPDBG()) { - return new PHPDBG; - } - - if ($runtime->hasXdebug()) { - return new Xdebug($filter); - } - - throw new RuntimeException('No code coverage driver available'); - } - - private function processUnintentionallyCoveredUnits(array $unintentionallyCoveredUnits): array - { - $unintentionallyCoveredUnits = \array_unique($unintentionallyCoveredUnits); - \sort($unintentionallyCoveredUnits); - - foreach (\array_keys($unintentionallyCoveredUnits) as $k => $v) { - $unit = \explode('::', $unintentionallyCoveredUnits[$k]); - - if (\count($unit) !== 2) { - continue; - } - - $class = new \ReflectionClass($unit[0]); - - foreach ($this->unintentionallyCoveredSubclassesWhitelist as $whitelisted) { - if ($class->isSubclassOf($whitelisted)) { - unset($unintentionallyCoveredUnits[$k]); - - break; - } - } - } - - return \array_values($unintentionallyCoveredUnits); - } - - /** - * @throws CoveredCodeNotExecutedException - * @throws InvalidArgumentException - * @throws MissingCoversAnnotationException - * @throws RuntimeException - * @throws UnintentionallyCoveredCodeException - * @throws \ReflectionException - */ - private function initializeData(): void - { - $this->isInitialized = true; - - if ($this->processUncoveredFilesFromWhitelist) { - $this->shouldCheckForDeadAndUnused = false; - - $this->driver->start(); - - foreach ($this->filter->getWhitelist() as $file) { - if ($this->filter->isFile($file)) { - include_once $file; - } - } - - $data = []; - $coverage = $this->driver->stop(); - - foreach ($coverage as $file => $fileCoverage) { - if ($this->filter->isFiltered($file)) { - continue; - } - - foreach (\array_keys($fileCoverage) as $key) { - if ($fileCoverage[$key] === Driver::LINE_EXECUTED) { - $fileCoverage[$key] = Driver::LINE_NOT_EXECUTED; - } - } - - $data[$file] = $fileCoverage; - } - - $this->append($data, 'UNCOVERED_FILES_FROM_WHITELIST'); - } - } - - private function coverageToCodeUnits(array $data): array - { - $codeUnits = []; - - foreach ($data as $filename => $lines) { - foreach ($lines as $line => $flag) { - if ($flag === 1) { - $codeUnits[] = $this->wizard->lookup($filename, $line); - } - } - } - - return \array_unique($codeUnits); - } - - private function linesToCodeUnits(array $data): array - { - $codeUnits = []; - - foreach ($data as $filename => $lines) { - foreach ($lines as $line) { - $codeUnits[] = $this->wizard->lookup($filename, $line); - } - } - - return \array_unique($codeUnits); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -use SebastianBergmann\Version as VersionId; - -final class Version -{ - /** - * @var string - */ - private static $version; - - public static function id(): string - { - if (self::$version === null) { - $version = new VersionId('6.1.4', \dirname(__DIR__)); - self::$version = $version->getVersion(); - } - - return self::$version; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Driver; - -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Driver for PHPDBG's code coverage functionality. - * - * @codeCoverageIgnore - */ -final class PHPDBG implements Driver -{ - /** - * @throws RuntimeException - */ - public function __construct() - { - if (\PHP_SAPI !== 'phpdbg') { - throw new RuntimeException( - 'This driver requires the PHPDBG SAPI' - ); - } - - if (!\function_exists('phpdbg_start_oplog')) { - throw new RuntimeException( - 'This build of PHPDBG does not support code coverage' - ); - } - } - - /** - * Start collection of code coverage information. - */ - public function start(bool $determineUnusedAndDead = true): void - { - \phpdbg_start_oplog(); - } - - /** - * Stop collection of code coverage information. - */ - public function stop(): array - { - static $fetchedLines = []; - - $dbgData = \phpdbg_end_oplog(); - - if ($fetchedLines == []) { - $sourceLines = \phpdbg_get_executable(); - } else { - $newFiles = \array_diff(\get_included_files(), \array_keys($fetchedLines)); - - $sourceLines = []; - - if ($newFiles) { - $sourceLines = phpdbg_get_executable(['files' => $newFiles]); - } - } - - foreach ($sourceLines as $file => $lines) { - foreach ($lines as $lineNo => $numExecuted) { - $sourceLines[$file][$lineNo] = self::LINE_NOT_EXECUTED; - } - } - - $fetchedLines = \array_merge($fetchedLines, $sourceLines); - - return $this->detectExecutedLines($fetchedLines, $dbgData); - } - - /** - * Convert phpdbg based data into the format CodeCoverage expects - */ - private function detectExecutedLines(array $sourceLines, array $dbgData): array - { - foreach ($dbgData as $file => $coveredLines) { - foreach ($coveredLines as $lineNo => $numExecuted) { - // phpdbg also reports $lineNo=0 when e.g. exceptions get thrown. - // make sure we only mark lines executed which are actually executable. - if (isset($sourceLines[$file][$lineNo])) { - $sourceLines[$file][$lineNo] = self::LINE_EXECUTED; - } - } - } - - return $sourceLines; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Driver; - -use SebastianBergmann\CodeCoverage\Filter; -use SebastianBergmann\CodeCoverage\RuntimeException; - -/** - * Driver for Xdebug's code coverage functionality. - * - * @codeCoverageIgnore - */ -final class Xdebug implements Driver -{ - /** - * @var array - */ - private $cacheNumLines = []; - - /** - * @var Filter - */ - private $filter; - - /** - * @throws RuntimeException - */ - public function __construct(Filter $filter = null) - { - if (!\extension_loaded('xdebug')) { - throw new RuntimeException('This driver requires Xdebug'); - } - - if (!\ini_get('xdebug.coverage_enable')) { - throw new RuntimeException('xdebug.coverage_enable=On has to be set in php.ini'); - } - - if ($filter === null) { - $filter = new Filter; - } - - $this->filter = $filter; - } - - /** - * Start collection of code coverage information. - */ - public function start(bool $determineUnusedAndDead = true): void - { - if ($determineUnusedAndDead) { - \xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE); - } else { - \xdebug_start_code_coverage(); - } - } - - /** - * Stop collection of code coverage information. - */ - public function stop(): array - { - $data = \xdebug_get_code_coverage(); - - \xdebug_stop_code_coverage(); - - return $this->cleanup($data); - } - - private function cleanup(array $data): array - { - foreach (\array_keys($data) as $file) { - unset($data[$file][0]); - - if (!$this->filter->isFile($file)) { - continue; - } - - $numLines = $this->getNumberOfLinesInFile($file); - - foreach (\array_keys($data[$file]) as $line) { - if ($line > $numLines) { - unset($data[$file][$line]); - } - } - } - - return $data; - } - - private function getNumberOfLinesInFile(string $fileName): int - { - if (!isset($this->cacheNumLines[$fileName])) { - $buffer = \file_get_contents($fileName); - $lines = \substr_count($buffer, "\n"); - - if (\substr($buffer, -1) !== "\n") { - $lines++; - } - - $this->cacheNumLines[$fileName] = $lines; - } - - return $this->cacheNumLines[$fileName]; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Driver; - -/** - * Interface for code coverage drivers. - */ -interface Driver -{ - /** - * @var int - * - * @see http://xdebug.org/docs/code_coverage - */ - public const LINE_EXECUTED = 1; - - /** - * @var int - * - * @see http://xdebug.org/docs/code_coverage - */ - public const LINE_NOT_EXECUTED = -1; - - /** - * @var int - * - * @see http://xdebug.org/docs/code_coverage - */ - public const LINE_NOT_EXECUTABLE = -2; - - /** - * Start collection of code coverage information. - */ - public function start(bool $determineUnusedAndDead = true): void; - - /** - * Stop collection of code coverage information. - */ - public function stop(): array; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -/** - * Utility methods. - */ -final class Util -{ - /** - * @return float|int|string - */ - public static function percent(float $a, float $b, bool $asString = false, bool $fixedWidth = false) - { - if ($asString && $b == 0) { - return ''; - } - - $percent = 100; - - if ($b > 0) { - $percent = ($a / $b) * 100; - } - - if ($asString) { - $format = $fixedWidth ? '%6.2F%%' : '%01.2F%%'; - - return \sprintf($format, $percent); - } - - return $percent; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -/** - * Recursive iterator for node object graphs. - */ -final class Iterator implements \RecursiveIterator -{ - /** - * @var int - */ - private $position; - - /** - * @var AbstractNode[] - */ - private $nodes; - - public function __construct(Directory $node) - { - $this->nodes = $node->getChildNodes(); - } - - /** - * Rewinds the Iterator to the first element. - */ - public function rewind(): void - { - $this->position = 0; - } - - /** - * Checks if there is a current element after calls to rewind() or next(). - */ - public function valid(): bool - { - return $this->position < \count($this->nodes); - } - - /** - * Returns the key of the current element. - */ - public function key(): int - { - return $this->position; - } - - /** - * Returns the current element. - */ - public function current(): AbstractNode - { - return $this->valid() ? $this->nodes[$this->position] : null; - } - - /** - * Moves forward to next element. - */ - public function next(): void - { - $this->position++; - } - - /** - * Returns the sub iterator for the current element. - * - * @return Iterator - */ - public function getChildren(): self - { - return new self($this->nodes[$this->position]); - } - - /** - * Checks whether the current element has children. - */ - public function hasChildren(): bool - { - return $this->nodes[$this->position] instanceof Directory; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -use SebastianBergmann\CodeCoverage\InvalidArgumentException; - -/** - * Represents a directory in the code coverage information tree. - */ -final class Directory extends AbstractNode implements \IteratorAggregate -{ - /** - * @var AbstractNode[] - */ - private $children = []; - - /** - * @var Directory[] - */ - private $directories = []; - - /** - * @var File[] - */ - private $files = []; - - /** - * @var array - */ - private $classes; - - /** - * @var array - */ - private $traits; - - /** - * @var array - */ - private $functions; - - /** - * @var array - */ - private $linesOfCode; - - /** - * @var int - */ - private $numFiles = -1; - - /** - * @var int - */ - private $numExecutableLines = -1; - - /** - * @var int - */ - private $numExecutedLines = -1; - - /** - * @var int - */ - private $numClasses = -1; - - /** - * @var int - */ - private $numTestedClasses = -1; - - /** - * @var int - */ - private $numTraits = -1; - - /** - * @var int - */ - private $numTestedTraits = -1; - - /** - * @var int - */ - private $numMethods = -1; - - /** - * @var int - */ - private $numTestedMethods = -1; - - /** - * @var int - */ - private $numFunctions = -1; - - /** - * @var int - */ - private $numTestedFunctions = -1; - - /** - * Returns the number of files in/under this node. - */ - public function count(): int - { - if ($this->numFiles === -1) { - $this->numFiles = 0; - - foreach ($this->children as $child) { - $this->numFiles += \count($child); - } - } - - return $this->numFiles; - } - - /** - * Returns an iterator for this node. - */ - public function getIterator(): \RecursiveIteratorIterator - { - return new \RecursiveIteratorIterator( - new Iterator($this), - \RecursiveIteratorIterator::SELF_FIRST - ); - } - - /** - * Adds a new directory. - */ - public function addDirectory(string $name): self - { - $directory = new self($name, $this); - - $this->children[] = $directory; - $this->directories[] = &$this->children[\count($this->children) - 1]; - - return $directory; - } - - /** - * Adds a new file. - * - * @throws InvalidArgumentException - */ - public function addFile(string $name, array $coverageData, array $testData, bool $cacheTokens): File - { - $file = new File($name, $this, $coverageData, $testData, $cacheTokens); - - $this->children[] = $file; - $this->files[] = &$this->children[\count($this->children) - 1]; - - $this->numExecutableLines = -1; - $this->numExecutedLines = -1; - - return $file; - } - - /** - * Returns the directories in this directory. - */ - public function getDirectories(): array - { - return $this->directories; - } - - /** - * Returns the files in this directory. - */ - public function getFiles(): array - { - return $this->files; - } - - /** - * Returns the child nodes of this node. - */ - public function getChildNodes(): array - { - return $this->children; - } - - /** - * Returns the classes of this node. - */ - public function getClasses(): array - { - if ($this->classes === null) { - $this->classes = []; - - foreach ($this->children as $child) { - $this->classes = \array_merge( - $this->classes, - $child->getClasses() - ); - } - } - - return $this->classes; - } - - /** - * Returns the traits of this node. - */ - public function getTraits(): array - { - if ($this->traits === null) { - $this->traits = []; - - foreach ($this->children as $child) { - $this->traits = \array_merge( - $this->traits, - $child->getTraits() - ); - } - } - - return $this->traits; - } - - /** - * Returns the functions of this node. - */ - public function getFunctions(): array - { - if ($this->functions === null) { - $this->functions = []; - - foreach ($this->children as $child) { - $this->functions = \array_merge( - $this->functions, - $child->getFunctions() - ); - } - } - - return $this->functions; - } - - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - public function getLinesOfCode(): array - { - if ($this->linesOfCode === null) { - $this->linesOfCode = ['loc' => 0, 'cloc' => 0, 'ncloc' => 0]; - - foreach ($this->children as $child) { - $linesOfCode = $child->getLinesOfCode(); - - $this->linesOfCode['loc'] += $linesOfCode['loc']; - $this->linesOfCode['cloc'] += $linesOfCode['cloc']; - $this->linesOfCode['ncloc'] += $linesOfCode['ncloc']; - } - } - - return $this->linesOfCode; - } - - /** - * Returns the number of executable lines. - */ - public function getNumExecutableLines(): int - { - if ($this->numExecutableLines === -1) { - $this->numExecutableLines = 0; - - foreach ($this->children as $child) { - $this->numExecutableLines += $child->getNumExecutableLines(); - } - } - - return $this->numExecutableLines; - } - - /** - * Returns the number of executed lines. - */ - public function getNumExecutedLines(): int - { - if ($this->numExecutedLines === -1) { - $this->numExecutedLines = 0; - - foreach ($this->children as $child) { - $this->numExecutedLines += $child->getNumExecutedLines(); - } - } - - return $this->numExecutedLines; - } - - /** - * Returns the number of classes. - */ - public function getNumClasses(): int - { - if ($this->numClasses === -1) { - $this->numClasses = 0; - - foreach ($this->children as $child) { - $this->numClasses += $child->getNumClasses(); - } - } - - return $this->numClasses; - } - - /** - * Returns the number of tested classes. - */ - public function getNumTestedClasses(): int - { - if ($this->numTestedClasses === -1) { - $this->numTestedClasses = 0; - - foreach ($this->children as $child) { - $this->numTestedClasses += $child->getNumTestedClasses(); - } - } - - return $this->numTestedClasses; - } - - /** - * Returns the number of traits. - */ - public function getNumTraits(): int - { - if ($this->numTraits === -1) { - $this->numTraits = 0; - - foreach ($this->children as $child) { - $this->numTraits += $child->getNumTraits(); - } - } - - return $this->numTraits; - } - - /** - * Returns the number of tested traits. - */ - public function getNumTestedTraits(): int - { - if ($this->numTestedTraits === -1) { - $this->numTestedTraits = 0; - - foreach ($this->children as $child) { - $this->numTestedTraits += $child->getNumTestedTraits(); - } - } - - return $this->numTestedTraits; - } - - /** - * Returns the number of methods. - */ - public function getNumMethods(): int - { - if ($this->numMethods === -1) { - $this->numMethods = 0; - - foreach ($this->children as $child) { - $this->numMethods += $child->getNumMethods(); - } - } - - return $this->numMethods; - } - - /** - * Returns the number of tested methods. - */ - public function getNumTestedMethods(): int - { - if ($this->numTestedMethods === -1) { - $this->numTestedMethods = 0; - - foreach ($this->children as $child) { - $this->numTestedMethods += $child->getNumTestedMethods(); - } - } - - return $this->numTestedMethods; - } - - /** - * Returns the number of functions. - */ - public function getNumFunctions(): int - { - if ($this->numFunctions === -1) { - $this->numFunctions = 0; - - foreach ($this->children as $child) { - $this->numFunctions += $child->getNumFunctions(); - } - } - - return $this->numFunctions; - } - - /** - * Returns the number of tested functions. - */ - public function getNumTestedFunctions(): int - { - if ($this->numTestedFunctions === -1) { - $this->numTestedFunctions = 0; - - foreach ($this->children as $child) { - $this->numTestedFunctions += $child->getNumTestedFunctions(); - } - } - - return $this->numTestedFunctions; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -use SebastianBergmann\CodeCoverage\CodeCoverage; - -final class Builder -{ - public function build(CodeCoverage $coverage): Directory - { - $files = $coverage->getData(); - $commonPath = $this->reducePaths($files); - $root = new Directory( - $commonPath, - null - ); - - $this->addItems( - $root, - $this->buildDirectoryStructure($files), - $coverage->getTests(), - $coverage->getCacheTokens() - ); - - return $root; - } - - private function addItems(Directory $root, array $items, array $tests, bool $cacheTokens): void - { - foreach ($items as $key => $value) { - if (\substr($key, -2) == '/f') { - $key = \substr($key, 0, -2); - - if (\file_exists($root->getPath() . \DIRECTORY_SEPARATOR . $key)) { - $root->addFile($key, $value, $tests, $cacheTokens); - } - } else { - $child = $root->addDirectory($key); - $this->addItems($child, $value, $tests, $cacheTokens); - } - } - } - - /** - * Builds an array representation of the directory structure. - * - * For instance, - * - * - * Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * is transformed into - * - * - * Array - * ( - * [.] => Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * ) - * - */ - private function buildDirectoryStructure(array $files): array - { - $result = []; - - foreach ($files as $path => $file) { - $path = \explode(\DIRECTORY_SEPARATOR, $path); - $pointer = &$result; - $max = \count($path); - - for ($i = 0; $i < $max; $i++) { - $type = ''; - - if ($i == ($max - 1)) { - $type = '/f'; - } - - $pointer = &$pointer[$path[$i] . $type]; - } - - $pointer = $file; - } - - return $result; - } - - /** - * Reduces the paths by cutting the longest common start path. - * - * For instance, - * - * - * Array - * ( - * [/home/sb/Money/Money.php] => Array - * ( - * ... - * ) - * - * [/home/sb/Money/MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - * - * is reduced to - * - * - * Array - * ( - * [Money.php] => Array - * ( - * ... - * ) - * - * [MoneyBag.php] => Array - * ( - * ... - * ) - * ) - * - */ - private function reducePaths(array &$files): string - { - if (empty($files)) { - return '.'; - } - - $commonPath = ''; - $paths = \array_keys($files); - - if (\count($files) === 1) { - $commonPath = \dirname($paths[0]) . \DIRECTORY_SEPARATOR; - $files[\basename($paths[0])] = $files[$paths[0]]; - - unset($files[$paths[0]]); - - return $commonPath; - } - - $max = \count($paths); - - for ($i = 0; $i < $max; $i++) { - // strip phar:// prefixes - if (\strpos($paths[$i], 'phar://') === 0) { - $paths[$i] = \substr($paths[$i], 7); - $paths[$i] = \str_replace('/', \DIRECTORY_SEPARATOR, $paths[$i]); - } - $paths[$i] = \explode(\DIRECTORY_SEPARATOR, $paths[$i]); - - if (empty($paths[$i][0])) { - $paths[$i][0] = \DIRECTORY_SEPARATOR; - } - } - - $done = false; - $max = \count($paths); - - while (!$done) { - for ($i = 0; $i < $max - 1; $i++) { - if (!isset($paths[$i][0]) || - !isset($paths[$i + 1][0]) || - $paths[$i][0] != $paths[$i + 1][0]) { - $done = true; - - break; - } - } - - if (!$done) { - $commonPath .= $paths[0][0]; - - if ($paths[0][0] != \DIRECTORY_SEPARATOR) { - $commonPath .= \DIRECTORY_SEPARATOR; - } - - for ($i = 0; $i < $max; $i++) { - \array_shift($paths[$i]); - } - } - } - - $original = \array_keys($files); - $max = \count($original); - - for ($i = 0; $i < $max; $i++) { - $files[\implode(\DIRECTORY_SEPARATOR, $paths[$i])] = $files[$original[$i]]; - unset($files[$original[$i]]); - } - - \ksort($files); - - return \substr($commonPath, 0, -1); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -/** - * Represents a file in the code coverage information tree. - */ -final class File extends AbstractNode -{ - /** - * @var array - */ - private $coverageData; - - /** - * @var array - */ - private $testData; - - /** - * @var int - */ - private $numExecutableLines = 0; - - /** - * @var int - */ - private $numExecutedLines = 0; - - /** - * @var array - */ - private $classes = []; - - /** - * @var array - */ - private $traits = []; - - /** - * @var array - */ - private $functions = []; - - /** - * @var array - */ - private $linesOfCode = []; - - /** - * @var int - */ - private $numClasses; - - /** - * @var int - */ - private $numTestedClasses = 0; - - /** - * @var int - */ - private $numTraits; - - /** - * @var int - */ - private $numTestedTraits = 0; - - /** - * @var int - */ - private $numMethods; - - /** - * @var int - */ - private $numTestedMethods; - - /** - * @var int - */ - private $numTestedFunctions; - - /** - * @var bool - */ - private $cacheTokens; - - /** - * @var array - */ - private $codeUnitsByLine = []; - - public function __construct(string $name, AbstractNode $parent, array $coverageData, array $testData, bool $cacheTokens) - { - parent::__construct($name, $parent); - - $this->coverageData = $coverageData; - $this->testData = $testData; - $this->cacheTokens = $cacheTokens; - - $this->calculateStatistics(); - } - - /** - * Returns the number of files in/under this node. - */ - public function count(): int - { - return 1; - } - - /** - * Returns the code coverage data of this node. - */ - public function getCoverageData(): array - { - return $this->coverageData; - } - - /** - * Returns the test data of this node. - */ - public function getTestData(): array - { - return $this->testData; - } - - /** - * Returns the classes of this node. - */ - public function getClasses(): array - { - return $this->classes; - } - - /** - * Returns the traits of this node. - */ - public function getTraits(): array - { - return $this->traits; - } - - /** - * Returns the functions of this node. - */ - public function getFunctions(): array - { - return $this->functions; - } - - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - public function getLinesOfCode(): array - { - return $this->linesOfCode; - } - - /** - * Returns the number of executable lines. - */ - public function getNumExecutableLines(): int - { - return $this->numExecutableLines; - } - - /** - * Returns the number of executed lines. - */ - public function getNumExecutedLines(): int - { - return $this->numExecutedLines; - } - - /** - * Returns the number of classes. - */ - public function getNumClasses(): int - { - if ($this->numClasses === null) { - $this->numClasses = 0; - - foreach ($this->classes as $class) { - foreach ($class['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numClasses++; - - continue 2; - } - } - } - } - - return $this->numClasses; - } - - /** - * Returns the number of tested classes. - */ - public function getNumTestedClasses(): int - { - return $this->numTestedClasses; - } - - /** - * Returns the number of traits. - */ - public function getNumTraits(): int - { - if ($this->numTraits === null) { - $this->numTraits = 0; - - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numTraits++; - - continue 2; - } - } - } - } - - return $this->numTraits; - } - - /** - * Returns the number of tested traits. - */ - public function getNumTestedTraits(): int - { - return $this->numTestedTraits; - } - - /** - * Returns the number of methods. - */ - public function getNumMethods(): int - { - if ($this->numMethods === null) { - $this->numMethods = 0; - - foreach ($this->classes as $class) { - foreach ($class['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numMethods++; - } - } - } - - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0) { - $this->numMethods++; - } - } - } - } - - return $this->numMethods; - } - - /** - * Returns the number of tested methods. - */ - public function getNumTestedMethods(): int - { - if ($this->numTestedMethods === null) { - $this->numTestedMethods = 0; - - foreach ($this->classes as $class) { - foreach ($class['methods'] as $method) { - if ($method['executableLines'] > 0 && - $method['coverage'] === 100) { - $this->numTestedMethods++; - } - } - } - - foreach ($this->traits as $trait) { - foreach ($trait['methods'] as $method) { - if ($method['executableLines'] > 0 && - $method['coverage'] === 100) { - $this->numTestedMethods++; - } - } - } - } - - return $this->numTestedMethods; - } - - /** - * Returns the number of functions. - */ - public function getNumFunctions(): int - { - return \count($this->functions); - } - - /** - * Returns the number of tested functions. - */ - public function getNumTestedFunctions(): int - { - if ($this->numTestedFunctions === null) { - $this->numTestedFunctions = 0; - - foreach ($this->functions as $function) { - if ($function['executableLines'] > 0 && - $function['coverage'] === 100) { - $this->numTestedFunctions++; - } - } - } - - return $this->numTestedFunctions; - } - - private function calculateStatistics(): void - { - if ($this->cacheTokens) { - $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); - } else { - $tokens = new \PHP_Token_Stream($this->getPath()); - } - - $this->linesOfCode = $tokens->getLinesOfCode(); - - foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = []; - } - - try { - $this->processClasses($tokens); - $this->processTraits($tokens); - $this->processFunctions($tokens); - } catch (\OutOfBoundsException $e) { - // This can happen with PHP_Token_Stream if the file is syntactically invalid, - // and probably affects a file that wasn't executed. - } - unset($tokens); - - foreach (\range(1, $this->linesOfCode['loc']) as $lineNumber) { - if (isset($this->coverageData[$lineNumber])) { - foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { - $codeUnit['executableLines']++; - } - - unset($codeUnit); - - $this->numExecutableLines++; - - if (\count($this->coverageData[$lineNumber]) > 0) { - foreach ($this->codeUnitsByLine[$lineNumber] as &$codeUnit) { - $codeUnit['executedLines']++; - } - - unset($codeUnit); - - $this->numExecutedLines++; - } - } - } - - foreach ($this->traits as &$trait) { - foreach ($trait['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } - - $method['crap'] = $this->crap( - $method['ccn'], - $method['coverage'] - ); - - $trait['ccn'] += $method['ccn']; - } - - unset($method); - - if ($trait['executableLines'] > 0) { - $trait['coverage'] = ($trait['executedLines'] / - $trait['executableLines']) * 100; - - if ($trait['coverage'] === 100) { - $this->numTestedClasses++; - } - } else { - $trait['coverage'] = 100; - } - - $trait['crap'] = $this->crap( - $trait['ccn'], - $trait['coverage'] - ); - } - - unset($trait); - - foreach ($this->classes as &$class) { - foreach ($class['methods'] as &$method) { - if ($method['executableLines'] > 0) { - $method['coverage'] = ($method['executedLines'] / - $method['executableLines']) * 100; - } else { - $method['coverage'] = 100; - } - - $method['crap'] = $this->crap( - $method['ccn'], - $method['coverage'] - ); - - $class['ccn'] += $method['ccn']; - } - - unset($method); - - if ($class['executableLines'] > 0) { - $class['coverage'] = ($class['executedLines'] / - $class['executableLines']) * 100; - - if ($class['coverage'] === 100) { - $this->numTestedClasses++; - } - } else { - $class['coverage'] = 100; - } - - $class['crap'] = $this->crap( - $class['ccn'], - $class['coverage'] - ); - } - - unset($class); - - foreach ($this->functions as &$function) { - if ($function['executableLines'] > 0) { - $function['coverage'] = ($function['executedLines'] / - $function['executableLines']) * 100; - } else { - $function['coverage'] = 100; - } - - if ($function['coverage'] === 100) { - $this->numTestedFunctions++; - } - - $function['crap'] = $this->crap( - $function['ccn'], - $function['coverage'] - ); - } - } - - private function processClasses(\PHP_Token_Stream $tokens): void - { - $classes = $tokens->getClasses(); - $link = $this->getId() . '.html#'; - - foreach ($classes as $className => $class) { - if (\strpos($className, 'anonymous') === 0) { - continue; - } - - if (!empty($class['package']['namespace'])) { - $className = $class['package']['namespace'] . '\\' . $className; - } - - $this->classes[$className] = [ - 'className' => $className, - 'methods' => [], - 'startLine' => $class['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'package' => $class['package'], - 'link' => $link . $class['startLine'], - ]; - - foreach ($class['methods'] as $methodName => $method) { - if (\strpos($methodName, 'anonymous') === 0) { - continue; - } - - $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); - - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [ - &$this->classes[$className], - &$this->classes[$className]['methods'][$methodName], - ]; - } - } - } - } - - private function processTraits(\PHP_Token_Stream $tokens): void - { - $traits = $tokens->getTraits(); - $link = $this->getId() . '.html#'; - - foreach ($traits as $traitName => $trait) { - $this->traits[$traitName] = [ - 'traitName' => $traitName, - 'methods' => [], - 'startLine' => $trait['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => 0, - 'coverage' => 0, - 'crap' => 0, - 'package' => $trait['package'], - 'link' => $link . $trait['startLine'], - ]; - - foreach ($trait['methods'] as $methodName => $method) { - if (\strpos($methodName, 'anonymous') === 0) { - continue; - } - - $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); - - foreach (\range($method['startLine'], $method['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [ - &$this->traits[$traitName], - &$this->traits[$traitName]['methods'][$methodName], - ]; - } - } - } - } - - private function processFunctions(\PHP_Token_Stream $tokens): void - { - $functions = $tokens->getFunctions(); - $link = $this->getId() . '.html#'; - - foreach ($functions as $functionName => $function) { - if (\strpos($functionName, 'anonymous') === 0) { - continue; - } - - $this->functions[$functionName] = [ - 'functionName' => $functionName, - 'signature' => $function['signature'], - 'startLine' => $function['startLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $function['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'link' => $link . $function['startLine'], - ]; - - foreach (\range($function['startLine'], $function['endLine']) as $lineNumber) { - $this->codeUnitsByLine[$lineNumber] = [&$this->functions[$functionName]]; - } - } - } - - private function crap(int $ccn, float $coverage): string - { - if ($coverage === 0) { - return (string) ($ccn ** 2 + $ccn); - } - - if ($coverage >= 95) { - return (string) $ccn; - } - - return \sprintf( - '%01.2F', - $ccn ** 2 * (1 - $coverage / 100) ** 3 + $ccn - ); - } - - private function newMethod(string $methodName, array $method, string $link): array - { - return [ - 'methodName' => $methodName, - 'visibility' => $method['visibility'], - 'signature' => $method['signature'], - 'startLine' => $method['startLine'], - 'endLine' => $method['endLine'], - 'executableLines' => 0, - 'executedLines' => 0, - 'ccn' => $method['ccn'], - 'coverage' => 0, - 'crap' => 0, - 'link' => $link . $method['startLine'], - ]; - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage\Node; - -use SebastianBergmann\CodeCoverage\Util; - -/** - * Base class for nodes in the code coverage information tree. - */ -abstract class AbstractNode implements \Countable -{ - /** - * @var string - */ - private $name; - - /** - * @var string - */ - private $path; - - /** - * @var array - */ - private $pathArray; - - /** - * @var AbstractNode - */ - private $parent; - - /** - * @var string - */ - private $id; - - public function __construct(string $name, self $parent = null) - { - if (\substr($name, -1) == \DIRECTORY_SEPARATOR) { - $name = \substr($name, 0, -1); - } - - $this->name = $name; - $this->parent = $parent; - } - - public function getName(): string - { - return $this->name; - } - - public function getId(): string - { - if ($this->id === null) { - $parent = $this->getParent(); - - if ($parent === null) { - $this->id = 'index'; - } else { - $parentId = $parent->getId(); - - if ($parentId === 'index') { - $this->id = \str_replace(':', '_', $this->name); - } else { - $this->id = $parentId . '/' . $this->name; - } - } - } - - return $this->id; - } - - public function getPath(): string - { - if ($this->path === null) { - if ($this->parent === null || $this->parent->getPath() === null || $this->parent->getPath() === false) { - $this->path = $this->name; - } else { - $this->path = $this->parent->getPath() . \DIRECTORY_SEPARATOR . $this->name; - } - } - - return $this->path; - } - - public function getPathAsArray(): array - { - if ($this->pathArray === null) { - if ($this->parent === null) { - $this->pathArray = []; - } else { - $this->pathArray = $this->parent->getPathAsArray(); - } - - $this->pathArray[] = $this; - } - - return $this->pathArray; - } - - public function getParent(): ?self - { - return $this->parent; - } - - /** - * Returns the percentage of classes that has been tested. - * - * @return int|string - */ - public function getTestedClassesPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedClasses(), - $this->getNumClasses(), - $asString - ); - } - - /** - * Returns the percentage of traits that has been tested. - * - * @return int|string - */ - public function getTestedTraitsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedTraits(), - $this->getNumTraits(), - $asString - ); - } - - /** - * Returns the percentage of classes and traits that has been tested. - * - * @return int|string - */ - public function getTestedClassesAndTraitsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedClassesAndTraits(), - $this->getNumClassesAndTraits(), - $asString - ); - } - - /** - * Returns the percentage of functions that has been tested. - * - * @return int|string - */ - public function getTestedFunctionsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedFunctions(), - $this->getNumFunctions(), - $asString - ); - } - - /** - * Returns the percentage of methods that has been tested. - * - * @return int|string - */ - public function getTestedMethodsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedMethods(), - $this->getNumMethods(), - $asString - ); - } - - /** - * Returns the percentage of functions and methods that has been tested. - * - * @return int|string - */ - public function getTestedFunctionsAndMethodsPercent(bool $asString = true) - { - return Util::percent( - $this->getNumTestedFunctionsAndMethods(), - $this->getNumFunctionsAndMethods(), - $asString - ); - } - - /** - * Returns the percentage of executed lines. - * - * @return int|string - */ - public function getLineExecutedPercent(bool $asString = true) - { - return Util::percent( - $this->getNumExecutedLines(), - $this->getNumExecutableLines(), - $asString - ); - } - - /** - * Returns the number of classes and traits. - */ - public function getNumClassesAndTraits(): int - { - return $this->getNumClasses() + $this->getNumTraits(); - } - - /** - * Returns the number of tested classes and traits. - */ - public function getNumTestedClassesAndTraits(): int - { - return $this->getNumTestedClasses() + $this->getNumTestedTraits(); - } - - /** - * Returns the classes and traits of this node. - */ - public function getClassesAndTraits(): array - { - return \array_merge($this->getClasses(), $this->getTraits()); - } - - /** - * Returns the number of functions and methods. - */ - public function getNumFunctionsAndMethods(): int - { - return $this->getNumFunctions() + $this->getNumMethods(); - } - - /** - * Returns the number of tested functions and methods. - */ - public function getNumTestedFunctionsAndMethods(): int - { - return $this->getNumTestedFunctions() + $this->getNumTestedMethods(); - } - - /** - * Returns the functions and methods of this node. - */ - public function getFunctionsAndMethods(): array - { - return \array_merge($this->getFunctions(), $this->getMethods()); - } - - /** - * Returns the classes of this node. - */ - abstract public function getClasses(): array; - - /** - * Returns the traits of this node. - */ - abstract public function getTraits(): array; - - /** - * Returns the functions of this node. - */ - abstract public function getFunctions(): array; - - /** - * Returns the LOC/CLOC/NCLOC of this node. - */ - abstract public function getLinesOfCode(): array; - - /** - * Returns the number of executable lines. - */ - abstract public function getNumExecutableLines(): int; - - /** - * Returns the number of executed lines. - */ - abstract public function getNumExecutedLines(): int; - - /** - * Returns the number of classes. - */ - abstract public function getNumClasses(): int; - - /** - * Returns the number of tested classes. - */ - abstract public function getNumTestedClasses(): int; - - /** - * Returns the number of traits. - */ - abstract public function getNumTraits(): int; - - /** - * Returns the number of tested traits. - */ - abstract public function getNumTestedTraits(): int; - - /** - * Returns the number of methods. - */ - abstract public function getNumMethods(): int; - - /** - * Returns the number of tested methods. - */ - abstract public function getNumTestedMethods(): int; - - /** - * Returns the number of functions. - */ - abstract public function getNumFunctions(): int; - - /** - * Returns the number of tested functions. - */ - abstract public function getNumTestedFunctions(): int; -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CodeCoverage; - -use SebastianBergmann\FileIterator\Facade as FileIteratorFacade; - -/** - * Filter for whitelisting of code coverage information. - */ -final class Filter -{ - /** - * Source files that are whitelisted. - * - * @var array - */ - private $whitelistedFiles = []; - - /** - * Remembers the result of the `is_file()` calls. - * - * @var bool[] - */ - private $isFileCallsCache = []; - - /** - * Adds a directory to the whitelist (recursively). - */ - public function addDirectoryToWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void - { - $facade = new FileIteratorFacade; - $files = $facade->getFilesAsArray($directory, $suffix, $prefix); - - foreach ($files as $file) { - $this->addFileToWhitelist($file); - } - } - - /** - * Adds a file to the whitelist. - */ - public function addFileToWhitelist(string $filename): void - { - $this->whitelistedFiles[\realpath($filename)] = true; - } - - /** - * Adds files to the whitelist. - * - * @param string[] $files - */ - public function addFilesToWhitelist(array $files): void - { - foreach ($files as $file) { - $this->addFileToWhitelist($file); - } - } - - /** - * Removes a directory from the whitelist (recursively). - */ - public function removeDirectoryFromWhitelist(string $directory, string $suffix = '.php', string $prefix = ''): void - { - $facade = new FileIteratorFacade; - $files = $facade->getFilesAsArray($directory, $suffix, $prefix); - - foreach ($files as $file) { - $this->removeFileFromWhitelist($file); - } - } - - /** - * Removes a file from the whitelist. - */ - public function removeFileFromWhitelist(string $filename): void - { - $filename = \realpath($filename); - - unset($this->whitelistedFiles[$filename]); - } - - /** - * Checks whether a filename is a real filename. - */ - public function isFile(string $filename): bool - { - if (isset($this->isFileCallsCache[$filename])) { - return $this->isFileCallsCache[$filename]; - } - - if ($filename === '-' || - \strpos($filename, 'vfs://') === 0 || - \strpos($filename, 'xdebug://debug-eval') !== false || - \strpos($filename, 'eval()\'d code') !== false || - \strpos($filename, 'runtime-created function') !== false || - \strpos($filename, 'runkit created function') !== false || - \strpos($filename, 'assert code') !== false || - \strpos($filename, 'regexp code') !== false || - \strpos($filename, 'Standard input code') !== false) { - $isFile = false; - } else { - $isFile = \file_exists($filename); - } - - $this->isFileCallsCache[$filename] = $isFile; - - return $isFile; - } - - /** - * Checks whether or not a file is filtered. - */ - public function isFiltered(string $filename): bool - { - if (!$this->isFile($filename)) { - return true; - } - - return !isset($this->whitelistedFiles[$filename]); - } - - /** - * Returns the list of whitelisted files. - * - * @return string[] - */ - public function getWhitelist(): array - { - return \array_keys($this->whitelistedFiles); - } - - /** - * Returns whether this filter has a whitelist. - */ - public function hasWhitelist(): bool - { - return !empty($this->whitelistedFiles); - } - - /** - * Returns the whitelisted files. - * - * @return string[] - */ - public function getWhitelistedFiles(): array - { - return $this->whitelistedFiles; - } - - /** - * Sets the whitelisted files. - */ - public function setWhitelistedFiles(array $whitelistedFiles): void - { - $this->whitelistedFiles = $whitelistedFiles; - } -} -php-code-coverage - -Copyright (c) 2009-2018, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -Exporter - -Copyright (c) 2002-2017, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace SebastianBergmann\Exporter; - -use SebastianBergmann\RecursionContext\Context; - -/** - * A nifty utility for visualizing PHP variables. - * - * - * export(new Exception); - * - */ -class Exporter -{ - /** - * Exports a value as a string - * - * The output of this method is similar to the output of print_r(), but - * improved in various aspects: - * - * - NULL is rendered as "null" (instead of "") - * - TRUE is rendered as "true" (instead of "1") - * - FALSE is rendered as "false" (instead of "") - * - Strings are always quoted with single quotes - * - Carriage returns and newlines are normalized to \n - * - Recursion and repeated rendering is treated properly - * - * @param mixed $value - * @param int $indentation The indentation level of the 2nd+ line - * - * @return string - */ - public function export($value, $indentation = 0) - { - return $this->recursiveExport($value, $indentation); - } - - /** - * @param mixed $data - * @param Context $context - * - * @return string - */ - public function shortenedRecursiveExport(&$data, Context $context = null) - { - $result = []; - $exporter = new self(); - - if (!$context) { - $context = new Context; - } - - $array = $data; - $context->add($data); - - foreach ($array as $key => $value) { - if (is_array($value)) { - if ($context->contains($data[$key]) !== false) { - $result[] = '*RECURSION*'; - } else { - $result[] = sprintf( - 'array(%s)', - $this->shortenedRecursiveExport($data[$key], $context) - ); - } - } else { - $result[] = $exporter->shortenedExport($value); - } - } - - return implode(', ', $result); - } - - /** - * Exports a value into a single-line string - * - * The output of this method is similar to the output of - * SebastianBergmann\Exporter\Exporter::export(). - * - * Newlines are replaced by the visible string '\n'. - * Contents of arrays and objects (if any) are replaced by '...'. - * - * @param mixed $value - * - * @return string - * - * @see SebastianBergmann\Exporter\Exporter::export - */ - public function shortenedExport($value) - { - if (is_string($value)) { - $string = str_replace("\n", '', $this->export($value)); - - if (function_exists('mb_strlen')) { - if (mb_strlen($string) > 40) { - $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7); - } - } else { - if (strlen($string) > 40) { - $string = substr($string, 0, 30) . '...' . substr($string, -7); - } - } - - return $string; - } - - if (is_object($value)) { - return sprintf( - '%s Object (%s)', - get_class($value), - count($this->toArray($value)) > 0 ? '...' : '' - ); - } - - if (is_array($value)) { - return sprintf( - 'Array (%s)', - count($value) > 0 ? '...' : '' - ); - } - - return $this->export($value); - } - - /** - * Converts an object to an array containing all of its private, protected - * and public properties. - * - * @param mixed $value - * - * @return array - */ - public function toArray($value) - { - if (!is_object($value)) { - return (array) $value; - } - - $array = []; - - foreach ((array) $value as $key => $val) { - // properties are transformed to keys in the following way: - // private $property => "\0Classname\0property" - // protected $property => "\0*\0property" - // public $property => "property" - if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { - $key = $matches[1]; - } - - // See https://github.com/php/php-src/commit/5721132 - if ($key === "\0gcdata") { - continue; - } - - $array[$key] = $val; - } - - // Some internal classes like SplObjectStorage don't work with the - // above (fast) mechanism nor with reflection in Zend. - // Format the output similarly to print_r() in this case - if ($value instanceof \SplObjectStorage) { - // However, the fast method does work in HHVM, and exposes the - // internal implementation. Hide it again. - if (property_exists('\SplObjectStorage', '__storage')) { - unset($array['__storage']); - } elseif (property_exists('\SplObjectStorage', 'storage')) { - unset($array['storage']); - } - - if (property_exists('\SplObjectStorage', '__key')) { - unset($array['__key']); - } - - foreach ($value as $key => $val) { - $array[spl_object_hash($val)] = [ - 'obj' => $val, - 'inf' => $value->getInfo(), - ]; - } - } - - return $array; - } - - /** - * Recursive implementation of export - * - * @param mixed $value The value to export - * @param int $indentation The indentation level of the 2nd+ line - * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects - * - * @return string - * - * @see SebastianBergmann\Exporter\Exporter::export - */ - protected function recursiveExport(&$value, $indentation, $processed = null) - { - if ($value === null) { - return 'null'; - } - - if ($value === true) { - return 'true'; - } - - if ($value === false) { - return 'false'; - } - - if (is_float($value) && floatval(intval($value)) === $value) { - return "$value.0"; - } - - if (is_resource($value)) { - return sprintf( - 'resource(%d) of type (%s)', - $value, - get_resource_type($value) - ); - } - - if (is_string($value)) { - // Match for most non printable chars somewhat taking multibyte chars into account - if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) { - return 'Binary String: 0x' . bin2hex($value); - } - - return "'" . - str_replace('', "\n", - str_replace( - ["\r\n", "\n\r", "\r", "\n"], - ['\r\n', '\n\r', '\r', '\n'], - $value - ) - ) . - "'"; - } - - $whitespace = str_repeat(' ', 4 * $indentation); - - if (!$processed) { - $processed = new Context; - } - - if (is_array($value)) { - if (($key = $processed->contains($value)) !== false) { - return 'Array &' . $key; - } - - $array = $value; - $key = $processed->add($value); - $values = ''; - - if (count($array) > 0) { - foreach ($array as $k => $v) { - $values .= sprintf( - '%s %s => %s' . "\n", - $whitespace, - $this->recursiveExport($k, $indentation), - $this->recursiveExport($value[$k], $indentation + 1, $processed) - ); - } - - $values = "\n" . $values . $whitespace; - } - - return sprintf('Array &%s (%s)', $key, $values); - } - - if (is_object($value)) { - $class = get_class($value); - - if ($hash = $processed->contains($value)) { - return sprintf('%s Object &%s', $class, $hash); - } - - $hash = $processed->add($value); - $values = ''; - $array = $this->toArray($value); - - if (count($array) > 0) { - foreach ($array as $k => $v) { - $values .= sprintf( - '%s %s => %s' . "\n", - $whitespace, - $this->recursiveExport($k, $indentation), - $this->recursiveExport($v, $indentation + 1, $processed) - ); - } - - $values = "\n" . $values . $whitespace; - } - - return sprintf('%s Object &%s (%s)', $class, $hash, $values); - } - - return var_export($value, true); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class VersionNumber { - /** - * @var int - */ - private $value; - - /** - * @param mixed $value - */ - public function __construct($value) { - if (is_numeric($value)) { - $this->value = $value; - } - } - - /** - * @return bool - */ - public function isAny() { - return $this->value === null; - } - - /** - * @return int - */ - public function getValue() { - return $this->value; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class Version { - /** - * @var VersionNumber - */ - private $major; - - /** - * @var VersionNumber - */ - private $minor; - - /** - * @var VersionNumber - */ - private $patch; - - /** - * @var PreReleaseSuffix - */ - private $preReleaseSuffix; - - /** - * @var string - */ - private $versionString = ''; - - /** - * @param string $versionString - */ - public function __construct($versionString) { - $this->ensureVersionStringIsValid($versionString); - - $this->versionString = $versionString; - } - - /** - * @return PreReleaseSuffix - */ - public function getPreReleaseSuffix() { - return $this->preReleaseSuffix; - } - - /** - * @return string - */ - public function getVersionString() { - return $this->versionString; - } - - /** - * @return bool - */ - public function hasPreReleaseSuffix() { - return $this->preReleaseSuffix !== null; - } - - /** - * @param Version $version - * - * @return bool - */ - public function isGreaterThan(Version $version) { - if ($version->getMajor()->getValue() > $this->getMajor()->getValue()) { - return false; - } - - if ($version->getMajor()->getValue() < $this->getMajor()->getValue()) { - return true; - } - - if ($version->getMinor()->getValue() > $this->getMinor()->getValue()) { - return false; - } - - if ($version->getMinor()->getValue() < $this->getMinor()->getValue()) { - return true; - } - - if ($version->getPatch()->getValue() > $this->getPatch()->getValue()) { - return false; - } - - if ($version->getPatch()->getValue() < $this->getPatch()->getValue()) { - return true; - } - - if (!$version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { - return false; - } - - if ($version->hasPreReleaseSuffix() && !$this->hasPreReleaseSuffix()) { - return true; - } - - if (!$version->hasPreReleaseSuffix() && $this->hasPreReleaseSuffix()) { - return false; - } - - return $this->getPreReleaseSuffix()->isGreaterThan($version->getPreReleaseSuffix()); - } - - /** - * @return VersionNumber - */ - public function getMajor() { - return $this->major; - } - - /** - * @return VersionNumber - */ - public function getMinor() { - return $this->minor; - } - - /** - * @return VersionNumber - */ - public function getPatch() { - return $this->patch; - } - - /** - * @param array $matches - */ - private function parseVersion(array $matches) { - $this->major = new VersionNumber($matches['Major']); - $this->minor = new VersionNumber($matches['Minor']); - $this->patch = isset($matches['Patch']) ? new VersionNumber($matches['Patch']) : new VersionNumber(null); - - if (isset($matches['PreReleaseSuffix'])) { - $this->preReleaseSuffix = new PreReleaseSuffix($matches['PreReleaseSuffix']); - } - } - - /** - * @param string $version - * - * @throws InvalidVersionException - */ - private function ensureVersionStringIsValid($version) { - $regex = '/^v? - (?(0|(?:[1-9][0-9]*))) - \\. - (?(0|(?:[1-9][0-9]*))) - (\\. - (?(0|(?:[1-9][0-9]*))) - )? - (?: - - - (?(?:(dev|beta|b|RC|alpha|a|patch|p)\.?\d*)) - )? - $/x'; - - if (preg_match($regex, $version, $matches) !== 1) { - throw new InvalidVersionException( - sprintf("Version string '%s' does not follow SemVer semantics", $version) - ); - } - - $this->parseVersion($matches); - } -} -versionString = $versionString; - - $this->parseVersion($versionString); - } - - /** - * @return string - */ - public function getLabel() { - return $this->label; - } - - /** - * @return string - */ - public function getBuildMetaData() { - return $this->buildMetaData; - } - - /** - * @return string - */ - public function getVersionString() { - return $this->versionString; - } - - /** - * @return VersionNumber - */ - public function getMajor() { - return $this->major; - } - - /** - * @return VersionNumber - */ - public function getMinor() { - return $this->minor; - } - - /** - * @return VersionNumber - */ - public function getPatch() { - return $this->patch; - } - - /** - * @param $versionString - */ - private function parseVersion($versionString) { - $this->extractBuildMetaData($versionString); - $this->extractLabel($versionString); - - $versionSegments = explode('.', $versionString); - $this->major = new VersionNumber($versionSegments[0]); - - $minorValue = isset($versionSegments[1]) ? $versionSegments[1] : null; - $patchValue = isset($versionSegments[2]) ? $versionSegments[2] : null; - - $this->minor = new VersionNumber($minorValue); - $this->patch = new VersionNumber($patchValue); - } - - /** - * @param string $versionString - */ - private function extractBuildMetaData(&$versionString) { - if (preg_match('/\+(.*)/', $versionString, $matches) == 1) { - $this->buildMetaData = $matches[1]; - $versionString = str_replace($matches[0], '', $versionString); - } - } - - /** - * @param string $versionString - */ - private function extractLabel(&$versionString) { - if (preg_match('/\-(.*)/', $versionString, $matches) == 1) { - $this->label = $matches[1]; - $versionString = str_replace($matches[0], '', $versionString); - } - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class SpecificMajorVersionConstraint extends AbstractVersionConstraint { - /** - * @var int - */ - private $major = 0; - - /** - * @param string $originalValue - * @param int $major - */ - public function __construct($originalValue, $major) { - parent::__construct($originalValue); - - $this->major = $major; - } - - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - return $version->getMajor()->getValue() == $this->major; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class OrVersionConstraintGroup extends AbstractVersionConstraint { - /** - * @var VersionConstraint[] - */ - private $constraints = []; - - /** - * @param string $originalValue - * @param VersionConstraint[] $constraints - */ - public function __construct($originalValue, array $constraints) { - parent::__construct($originalValue); - - $this->constraints = $constraints; - } - - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - foreach ($this->constraints as $constraint) { - if ($constraint->complies($version)) { - return true; - } - } - - return false; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class SpecificMajorAndMinorVersionConstraint extends AbstractVersionConstraint { - /** - * @var int - */ - private $major = 0; - - /** - * @var int - */ - private $minor = 0; - - /** - * @param string $originalValue - * @param int $major - * @param int $minor - */ - public function __construct($originalValue, $major, $minor) { - parent::__construct($originalValue); - - $this->major = $major; - $this->minor = $minor; - } - - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - if ($version->getMajor()->getValue() != $this->major) { - return false; - } - - return $version->getMinor()->getValue() == $this->minor; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class AndVersionConstraintGroup extends AbstractVersionConstraint { - /** - * @var VersionConstraint[] - */ - private $constraints = []; - - /** - * @param string $originalValue - * @param VersionConstraint[] $constraints - */ - public function __construct($originalValue, array $constraints) { - parent::__construct($originalValue); - - $this->constraints = $constraints; - } - - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - foreach ($this->constraints as $constraint) { - if (!$constraint->complies($version)) { - return false; - } - } - - return true; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -abstract class AbstractVersionConstraint implements VersionConstraint { - /** - * @var string - */ - private $originalValue = ''; - - /** - * @param string $originalValue - */ - public function __construct($originalValue) { - $this->originalValue = $originalValue; - } - - /** - * @return string - */ - public function asString() { - return $this->originalValue; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class ExactVersionConstraint extends AbstractVersionConstraint { - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - return $this->asString() == $version->getVersionString(); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -interface VersionConstraint { - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version); - - /** - * @return string - */ - public function asString(); - -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class GreaterThanOrEqualToVersionConstraint extends AbstractVersionConstraint { - /** - * @var Version - */ - private $minimalVersion; - - /** - * @param string $originalValue - * @param Version $minimalVersion - */ - public function __construct($originalValue, Version $minimalVersion) { - parent::__construct($originalValue); - - $this->minimalVersion = $minimalVersion; - } - - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - return $version->getVersionString() == $this->minimalVersion->getVersionString() - || $version->isGreaterThan($this->minimalVersion); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class AnyVersionConstraint implements VersionConstraint { - /** - * @param Version $version - * - * @return bool - */ - public function complies(Version $version) { - return true; - } - - /** - * @return string - */ - public function asString() { - return '*'; - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -class VersionConstraintParser { - /** - * @param string $value - * - * @return VersionConstraint - * - * @throws UnsupportedVersionConstraintException - */ - public function parse($value) { - - if (strpos($value, '||') !== false) { - return $this->handleOrGroup($value); - } - - if (!preg_match('/^[\^~\*]?[\d.\*]+(?:-.*)?$/', $value)) { - throw new UnsupportedVersionConstraintException( - sprintf('Version constraint %s is not supported.', $value) - ); - } - - switch ($value[0]) { - case '~': - return $this->handleTildeOperator($value); - case '^': - return $this->handleCaretOperator($value); - } - - $version = new VersionConstraintValue($value); - - if ($version->getMajor()->isAny()) { - return new AnyVersionConstraint(); - } - - if ($version->getMinor()->isAny()) { - return new SpecificMajorVersionConstraint( - $version->getVersionString(), - $version->getMajor()->getValue() - ); - } - - if ($version->getPatch()->isAny()) { - return new SpecificMajorAndMinorVersionConstraint( - $version->getVersionString(), - $version->getMajor()->getValue(), - $version->getMinor()->getValue() - ); - } - - return new ExactVersionConstraint($version->getVersionString()); - } - - /** - * @param $value - * - * @return OrVersionConstraintGroup - */ - private function handleOrGroup($value) { - $constraints = []; - - foreach (explode('||', $value) as $groupSegment) { - $constraints[] = $this->parse(trim($groupSegment)); - } - - return new OrVersionConstraintGroup($value, $constraints); - } - - /** - * @param string $value - * - * @return AndVersionConstraintGroup - */ - private function handleTildeOperator($value) { - $version = new Version(substr($value, 1)); - $constraints = [ - new GreaterThanOrEqualToVersionConstraint($value, $version) - ]; - - if ($version->getPatch()->isAny()) { - $constraints[] = new SpecificMajorVersionConstraint( - $value, - $version->getMajor()->getValue() - ); - } else { - $constraints[] = new SpecificMajorAndMinorVersionConstraint( - $value, - $version->getMajor()->getValue(), - $version->getMinor()->getValue() - ); - } - - return new AndVersionConstraintGroup($value, $constraints); - } - - /** - * @param string $value - * - * @return AndVersionConstraintGroup - */ - private function handleCaretOperator($value) { - $version = new Version(substr($value, 1)); - - return new AndVersionConstraintGroup( - $value, - [ - new GreaterThanOrEqualToVersionConstraint($value, $version), - new SpecificMajorVersionConstraint($value, $version->getMajor()->getValue()) - ] - ); - } -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -interface Exception { -} -, Sebastian Heuer , Sebastian Bergmann - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PharIo\Version; - -final class UnsupportedVersionConstraintException extends \RuntimeException implements Exception { -} -phar-io/version - -Copyright (c) 2016-2017 Arne Blankerts , Sebastian Heuer and contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Arne Blankerts nor the names of contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - 0, - 'a' => 1, - 'alpha' => 1, - 'b' => 2, - 'beta' => 2, - 'rc' => 3, - 'p' => 4, - 'patch' => 4, - ]; - - /** - * @var string - */ - private $value; - - /** - * @var int - */ - private $valueScore; - - /** - * @var int - */ - private $number = 0; - - /** - * @param string $value - */ - public function __construct($value) { - $this->parseValue($value); - } - - /** - * @return string - */ - public function getValue() { - return $this->value; - } - - /** - * @return int|null - */ - public function getNumber() { - return $this->number; - } - - /** - * @param PreReleaseSuffix $suffix - * - * @return bool - */ - public function isGreaterThan(PreReleaseSuffix $suffix) { - if ($this->valueScore > $suffix->valueScore) { - return true; - } - - if ($this->valueScore < $suffix->valueScore) { - return false; - } - - return $this->getNumber() > $suffix->getNumber(); - } - - /** - * @param $value - * - * @return int - */ - private function mapValueToScore($value) { - if (array_key_exists($value, $this->valueScoreMap)) { - return $this->valueScoreMap[$value]; - } - - return 0; - } - - private function parseValue($value) { - $regex = '/-?(dev|beta|b|rc|alpha|a|patch|p)\.?(\d*).*$/i'; - if (preg_match($regex, $value, $matches) !== 1) { - throw new InvalidPreReleaseSuffixException(sprintf('Invalid label %s', $value)); - } - - $this->value = $matches[1]; - if (isset($matches[2])) { - $this->number = (int)$matches[2]; - } - $this->valueScore = $this->mapValueToScore($this->value); - } -} - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\ResourceOperations; - -final class ResourceOperations -{ - /** - * @return string[] - */ - public static function getFunctions(): array - { - return [ - 'Directory::close', - 'Directory::read', - 'Directory::rewind', - 'DirectoryIterator::openFile', - 'FilesystemIterator::openFile', - 'Gmagick::readimagefile', - 'HttpResponse::getRequestBodyStream', - 'HttpResponse::getStream', - 'HttpResponse::setStream', - 'Imagick::pingImageFile', - 'Imagick::readImageFile', - 'Imagick::writeImageFile', - 'Imagick::writeImagesFile', - 'MongoGridFSCursor::__construct', - 'MongoGridFSFile::getResource', - 'MysqlndUhConnection::stmtInit', - 'MysqlndUhConnection::storeResult', - 'MysqlndUhConnection::useResult', - 'PDF_activate_item', - 'PDF_add_launchlink', - 'PDF_add_locallink', - 'PDF_add_nameddest', - 'PDF_add_note', - 'PDF_add_pdflink', - 'PDF_add_table_cell', - 'PDF_add_textflow', - 'PDF_add_thumbnail', - 'PDF_add_weblink', - 'PDF_arc', - 'PDF_arcn', - 'PDF_attach_file', - 'PDF_begin_document', - 'PDF_begin_font', - 'PDF_begin_glyph', - 'PDF_begin_item', - 'PDF_begin_layer', - 'PDF_begin_page', - 'PDF_begin_page_ext', - 'PDF_begin_pattern', - 'PDF_begin_template', - 'PDF_begin_template_ext', - 'PDF_circle', - 'PDF_clip', - 'PDF_close', - 'PDF_close_image', - 'PDF_close_pdi', - 'PDF_close_pdi_page', - 'PDF_closepath', - 'PDF_closepath_fill_stroke', - 'PDF_closepath_stroke', - 'PDF_concat', - 'PDF_continue_text', - 'PDF_create_3dview', - 'PDF_create_action', - 'PDF_create_annotation', - 'PDF_create_bookmark', - 'PDF_create_field', - 'PDF_create_fieldgroup', - 'PDF_create_gstate', - 'PDF_create_pvf', - 'PDF_create_textflow', - 'PDF_curveto', - 'PDF_define_layer', - 'PDF_delete', - 'PDF_delete_pvf', - 'PDF_delete_table', - 'PDF_delete_textflow', - 'PDF_encoding_set_char', - 'PDF_end_document', - 'PDF_end_font', - 'PDF_end_glyph', - 'PDF_end_item', - 'PDF_end_layer', - 'PDF_end_page', - 'PDF_end_page_ext', - 'PDF_end_pattern', - 'PDF_end_template', - 'PDF_endpath', - 'PDF_fill', - 'PDF_fill_imageblock', - 'PDF_fill_pdfblock', - 'PDF_fill_stroke', - 'PDF_fill_textblock', - 'PDF_findfont', - 'PDF_fit_image', - 'PDF_fit_pdi_page', - 'PDF_fit_table', - 'PDF_fit_textflow', - 'PDF_fit_textline', - 'PDF_get_apiname', - 'PDF_get_buffer', - 'PDF_get_errmsg', - 'PDF_get_errnum', - 'PDF_get_parameter', - 'PDF_get_pdi_parameter', - 'PDF_get_pdi_value', - 'PDF_get_value', - 'PDF_info_font', - 'PDF_info_matchbox', - 'PDF_info_table', - 'PDF_info_textflow', - 'PDF_info_textline', - 'PDF_initgraphics', - 'PDF_lineto', - 'PDF_load_3ddata', - 'PDF_load_font', - 'PDF_load_iccprofile', - 'PDF_load_image', - 'PDF_makespotcolor', - 'PDF_moveto', - 'PDF_new', - 'PDF_open_ccitt', - 'PDF_open_file', - 'PDF_open_image', - 'PDF_open_image_file', - 'PDF_open_memory_image', - 'PDF_open_pdi', - 'PDF_open_pdi_document', - 'PDF_open_pdi_page', - 'PDF_pcos_get_number', - 'PDF_pcos_get_stream', - 'PDF_pcos_get_string', - 'PDF_place_image', - 'PDF_place_pdi_page', - 'PDF_process_pdi', - 'PDF_rect', - 'PDF_restore', - 'PDF_resume_page', - 'PDF_rotate', - 'PDF_save', - 'PDF_scale', - 'PDF_set_border_color', - 'PDF_set_border_dash', - 'PDF_set_border_style', - 'PDF_set_gstate', - 'PDF_set_info', - 'PDF_set_layer_dependency', - 'PDF_set_parameter', - 'PDF_set_text_pos', - 'PDF_set_value', - 'PDF_setcolor', - 'PDF_setdash', - 'PDF_setdashpattern', - 'PDF_setflat', - 'PDF_setfont', - 'PDF_setgray', - 'PDF_setgray_fill', - 'PDF_setgray_stroke', - 'PDF_setlinecap', - 'PDF_setlinejoin', - 'PDF_setlinewidth', - 'PDF_setmatrix', - 'PDF_setmiterlimit', - 'PDF_setrgbcolor', - 'PDF_setrgbcolor_fill', - 'PDF_setrgbcolor_stroke', - 'PDF_shading', - 'PDF_shading_pattern', - 'PDF_shfill', - 'PDF_show', - 'PDF_show_boxed', - 'PDF_show_xy', - 'PDF_skew', - 'PDF_stringwidth', - 'PDF_stroke', - 'PDF_suspend_page', - 'PDF_translate', - 'PDF_utf16_to_utf8', - 'PDF_utf32_to_utf16', - 'PDF_utf8_to_utf16', - 'PDO::pgsqlLOBOpen', - 'RarEntry::getStream', - 'SQLite3::openBlob', - 'SWFMovie::saveToFile', - 'SplFileInfo::openFile', - 'SplFileObject::openFile', - 'SplTempFileObject::openFile', - 'V8Js::compileString', - 'V8Js::executeScript', - 'Vtiful\Kernel\Excel::setColumn', - 'Vtiful\Kernel\Excel::setRow', - 'Vtiful\Kernel\Format::align', - 'Vtiful\Kernel\Format::bold', - 'Vtiful\Kernel\Format::italic', - 'Vtiful\Kernel\Format::underline', - 'XMLWriter::openMemory', - 'XMLWriter::openURI', - 'ZipArchive::getStream', - 'Zookeeper::setLogStream', - 'apc_bin_dumpfile', - 'apc_bin_loadfile', - 'bbcode_add_element', - 'bbcode_add_smiley', - 'bbcode_create', - 'bbcode_destroy', - 'bbcode_parse', - 'bbcode_set_arg_parser', - 'bbcode_set_flags', - 'bcompiler_read', - 'bcompiler_write_class', - 'bcompiler_write_constant', - 'bcompiler_write_exe_footer', - 'bcompiler_write_file', - 'bcompiler_write_footer', - 'bcompiler_write_function', - 'bcompiler_write_functions_from_file', - 'bcompiler_write_header', - 'bcompiler_write_included_filename', - 'bzclose', - 'bzerrno', - 'bzerror', - 'bzerrstr', - 'bzflush', - 'bzopen', - 'bzread', - 'bzwrite', - 'cairo_surface_write_to_png', - 'closedir', - 'copy', - 'crack_closedict', - 'crack_opendict', - 'cubrid_bind', - 'cubrid_close_prepare', - 'cubrid_close_request', - 'cubrid_col_get', - 'cubrid_col_size', - 'cubrid_column_names', - 'cubrid_column_types', - 'cubrid_commit', - 'cubrid_connect', - 'cubrid_connect_with_url', - 'cubrid_current_oid', - 'cubrid_db_parameter', - 'cubrid_disconnect', - 'cubrid_drop', - 'cubrid_fetch', - 'cubrid_free_result', - 'cubrid_get', - 'cubrid_get_autocommit', - 'cubrid_get_charset', - 'cubrid_get_class_name', - 'cubrid_get_db_parameter', - 'cubrid_get_query_timeout', - 'cubrid_get_server_info', - 'cubrid_insert_id', - 'cubrid_is_instance', - 'cubrid_lob2_bind', - 'cubrid_lob2_close', - 'cubrid_lob2_export', - 'cubrid_lob2_import', - 'cubrid_lob2_new', - 'cubrid_lob2_read', - 'cubrid_lob2_seek', - 'cubrid_lob2_seek64', - 'cubrid_lob2_size', - 'cubrid_lob2_size64', - 'cubrid_lob2_tell', - 'cubrid_lob2_tell64', - 'cubrid_lob2_write', - 'cubrid_lob_export', - 'cubrid_lob_get', - 'cubrid_lob_send', - 'cubrid_lob_size', - 'cubrid_lock_read', - 'cubrid_lock_write', - 'cubrid_move_cursor', - 'cubrid_next_result', - 'cubrid_num_cols', - 'cubrid_num_rows', - 'cubrid_pconnect', - 'cubrid_pconnect_with_url', - 'cubrid_prepare', - 'cubrid_put', - 'cubrid_query', - 'cubrid_rollback', - 'cubrid_schema', - 'cubrid_seq_add', - 'cubrid_seq_drop', - 'cubrid_seq_insert', - 'cubrid_seq_put', - 'cubrid_set_add', - 'cubrid_set_autocommit', - 'cubrid_set_db_parameter', - 'cubrid_set_drop', - 'cubrid_set_query_timeout', - 'cubrid_unbuffered_query', - 'curl_close', - 'curl_copy_handle', - 'curl_errno', - 'curl_error', - 'curl_escape', - 'curl_exec', - 'curl_getinfo', - 'curl_multi_add_handle', - 'curl_multi_close', - 'curl_multi_errno', - 'curl_multi_exec', - 'curl_multi_getcontent', - 'curl_multi_info_read', - 'curl_multi_remove_handle', - 'curl_multi_select', - 'curl_multi_setopt', - 'curl_pause', - 'curl_reset', - 'curl_setopt', - 'curl_setopt_array', - 'curl_share_close', - 'curl_share_errno', - 'curl_share_init', - 'curl_share_setopt', - 'curl_unescape', - 'cyrus_authenticate', - 'cyrus_bind', - 'cyrus_close', - 'cyrus_connect', - 'cyrus_query', - 'cyrus_unbind', - 'db2_autocommit', - 'db2_bind_param', - 'db2_client_info', - 'db2_close', - 'db2_column_privileges', - 'db2_columns', - 'db2_commit', - 'db2_conn_error', - 'db2_conn_errormsg', - 'db2_connect', - 'db2_cursor_type', - 'db2_exec', - 'db2_execute', - 'db2_fetch_array', - 'db2_fetch_assoc', - 'db2_fetch_both', - 'db2_fetch_object', - 'db2_fetch_row', - 'db2_field_display_size', - 'db2_field_name', - 'db2_field_num', - 'db2_field_precision', - 'db2_field_scale', - 'db2_field_type', - 'db2_field_width', - 'db2_foreign_keys', - 'db2_free_result', - 'db2_free_stmt', - 'db2_get_option', - 'db2_last_insert_id', - 'db2_lob_read', - 'db2_next_result', - 'db2_num_fields', - 'db2_num_rows', - 'db2_pclose', - 'db2_pconnect', - 'db2_prepare', - 'db2_primary_keys', - 'db2_procedure_columns', - 'db2_procedures', - 'db2_result', - 'db2_rollback', - 'db2_server_info', - 'db2_set_option', - 'db2_special_columns', - 'db2_statistics', - 'db2_stmt_error', - 'db2_stmt_errormsg', - 'db2_table_privileges', - 'db2_tables', - 'dba_close', - 'dba_delete', - 'dba_exists', - 'dba_fetch', - 'dba_firstkey', - 'dba_insert', - 'dba_nextkey', - 'dba_open', - 'dba_optimize', - 'dba_popen', - 'dba_replace', - 'dba_sync', - 'dbplus_add', - 'dbplus_aql', - 'dbplus_close', - 'dbplus_curr', - 'dbplus_find', - 'dbplus_first', - 'dbplus_flush', - 'dbplus_freelock', - 'dbplus_freerlocks', - 'dbplus_getlock', - 'dbplus_getunique', - 'dbplus_info', - 'dbplus_last', - 'dbplus_lockrel', - 'dbplus_next', - 'dbplus_open', - 'dbplus_prev', - 'dbplus_rchperm', - 'dbplus_rcreate', - 'dbplus_rcrtexact', - 'dbplus_rcrtlike', - 'dbplus_restorepos', - 'dbplus_rkeys', - 'dbplus_ropen', - 'dbplus_rquery', - 'dbplus_rrename', - 'dbplus_rsecindex', - 'dbplus_runlink', - 'dbplus_rzap', - 'dbplus_savepos', - 'dbplus_setindex', - 'dbplus_setindexbynumber', - 'dbplus_sql', - 'dbplus_tremove', - 'dbplus_undo', - 'dbplus_undoprepare', - 'dbplus_unlockrel', - 'dbplus_unselect', - 'dbplus_update', - 'dbplus_xlockrel', - 'dbplus_xunlockrel', - 'deflate_add', - 'dio_close', - 'dio_fcntl', - 'dio_open', - 'dio_read', - 'dio_seek', - 'dio_stat', - 'dio_tcsetattr', - 'dio_truncate', - 'dio_write', - 'dir', - 'eio_busy', - 'eio_cancel', - 'eio_chmod', - 'eio_chown', - 'eio_close', - 'eio_custom', - 'eio_dup2', - 'eio_fallocate', - 'eio_fchmod', - 'eio_fchown', - 'eio_fdatasync', - 'eio_fstat', - 'eio_fstatvfs', - 'eio_fsync', - 'eio_ftruncate', - 'eio_futime', - 'eio_get_last_error', - 'eio_grp', - 'eio_grp_add', - 'eio_grp_cancel', - 'eio_grp_limit', - 'eio_link', - 'eio_lstat', - 'eio_mkdir', - 'eio_mknod', - 'eio_nop', - 'eio_open', - 'eio_read', - 'eio_readahead', - 'eio_readdir', - 'eio_readlink', - 'eio_realpath', - 'eio_rename', - 'eio_rmdir', - 'eio_seek', - 'eio_sendfile', - 'eio_stat', - 'eio_statvfs', - 'eio_symlink', - 'eio_sync', - 'eio_sync_file_range', - 'eio_syncfs', - 'eio_truncate', - 'eio_unlink', - 'eio_utime', - 'eio_write', - 'enchant_broker_describe', - 'enchant_broker_dict_exists', - 'enchant_broker_free', - 'enchant_broker_free_dict', - 'enchant_broker_get_dict_path', - 'enchant_broker_get_error', - 'enchant_broker_init', - 'enchant_broker_list_dicts', - 'enchant_broker_request_dict', - 'enchant_broker_request_pwl_dict', - 'enchant_broker_set_dict_path', - 'enchant_broker_set_ordering', - 'enchant_dict_add_to_personal', - 'enchant_dict_add_to_session', - 'enchant_dict_check', - 'enchant_dict_describe', - 'enchant_dict_get_error', - 'enchant_dict_is_in_session', - 'enchant_dict_quick_check', - 'enchant_dict_store_replacement', - 'enchant_dict_suggest', - 'event_add', - 'event_base_free', - 'event_base_loop', - 'event_base_loopbreak', - 'event_base_loopexit', - 'event_base_new', - 'event_base_priority_init', - 'event_base_reinit', - 'event_base_set', - 'event_buffer_base_set', - 'event_buffer_disable', - 'event_buffer_enable', - 'event_buffer_fd_set', - 'event_buffer_free', - 'event_buffer_new', - 'event_buffer_priority_set', - 'event_buffer_read', - 'event_buffer_set_callback', - 'event_buffer_timeout_set', - 'event_buffer_watermark_set', - 'event_buffer_write', - 'event_del', - 'event_free', - 'event_new', - 'event_priority_set', - 'event_set', - 'event_timer_add', - 'event_timer_del', - 'event_timer_pending', - 'event_timer_set', - 'expect_expectl', - 'expect_popen', - 'fam_cancel_monitor', - 'fam_close', - 'fam_monitor_collection', - 'fam_monitor_directory', - 'fam_monitor_file', - 'fam_next_event', - 'fam_open', - 'fam_pending', - 'fam_resume_monitor', - 'fam_suspend_monitor', - 'fann_cascadetrain_on_data', - 'fann_cascadetrain_on_file', - 'fann_clear_scaling_params', - 'fann_copy', - 'fann_create_from_file', - 'fann_create_shortcut_array', - 'fann_create_standard', - 'fann_create_standard_array', - 'fann_create_train', - 'fann_create_train_from_callback', - 'fann_descale_input', - 'fann_descale_output', - 'fann_descale_train', - 'fann_destroy', - 'fann_destroy_train', - 'fann_duplicate_train_data', - 'fann_get_MSE', - 'fann_get_activation_function', - 'fann_get_activation_steepness', - 'fann_get_bias_array', - 'fann_get_bit_fail', - 'fann_get_bit_fail_limit', - 'fann_get_cascade_activation_functions', - 'fann_get_cascade_activation_functions_count', - 'fann_get_cascade_activation_steepnesses', - 'fann_get_cascade_activation_steepnesses_count', - 'fann_get_cascade_candidate_change_fraction', - 'fann_get_cascade_candidate_limit', - 'fann_get_cascade_candidate_stagnation_epochs', - 'fann_get_cascade_max_cand_epochs', - 'fann_get_cascade_max_out_epochs', - 'fann_get_cascade_min_cand_epochs', - 'fann_get_cascade_min_out_epochs', - 'fann_get_cascade_num_candidate_groups', - 'fann_get_cascade_num_candidates', - 'fann_get_cascade_output_change_fraction', - 'fann_get_cascade_output_stagnation_epochs', - 'fann_get_cascade_weight_multiplier', - 'fann_get_connection_array', - 'fann_get_connection_rate', - 'fann_get_errno', - 'fann_get_errstr', - 'fann_get_layer_array', - 'fann_get_learning_momentum', - 'fann_get_learning_rate', - 'fann_get_network_type', - 'fann_get_num_input', - 'fann_get_num_layers', - 'fann_get_num_output', - 'fann_get_quickprop_decay', - 'fann_get_quickprop_mu', - 'fann_get_rprop_decrease_factor', - 'fann_get_rprop_delta_max', - 'fann_get_rprop_delta_min', - 'fann_get_rprop_delta_zero', - 'fann_get_rprop_increase_factor', - 'fann_get_sarprop_step_error_shift', - 'fann_get_sarprop_step_error_threshold_factor', - 'fann_get_sarprop_temperature', - 'fann_get_sarprop_weight_decay_shift', - 'fann_get_total_connections', - 'fann_get_total_neurons', - 'fann_get_train_error_function', - 'fann_get_train_stop_function', - 'fann_get_training_algorithm', - 'fann_init_weights', - 'fann_length_train_data', - 'fann_merge_train_data', - 'fann_num_input_train_data', - 'fann_num_output_train_data', - 'fann_randomize_weights', - 'fann_read_train_from_file', - 'fann_reset_errno', - 'fann_reset_errstr', - 'fann_run', - 'fann_save', - 'fann_save_train', - 'fann_scale_input', - 'fann_scale_input_train_data', - 'fann_scale_output', - 'fann_scale_output_train_data', - 'fann_scale_train', - 'fann_scale_train_data', - 'fann_set_activation_function', - 'fann_set_activation_function_hidden', - 'fann_set_activation_function_layer', - 'fann_set_activation_function_output', - 'fann_set_activation_steepness', - 'fann_set_activation_steepness_hidden', - 'fann_set_activation_steepness_layer', - 'fann_set_activation_steepness_output', - 'fann_set_bit_fail_limit', - 'fann_set_callback', - 'fann_set_cascade_activation_functions', - 'fann_set_cascade_activation_steepnesses', - 'fann_set_cascade_candidate_change_fraction', - 'fann_set_cascade_candidate_limit', - 'fann_set_cascade_candidate_stagnation_epochs', - 'fann_set_cascade_max_cand_epochs', - 'fann_set_cascade_max_out_epochs', - 'fann_set_cascade_min_cand_epochs', - 'fann_set_cascade_min_out_epochs', - 'fann_set_cascade_num_candidate_groups', - 'fann_set_cascade_output_change_fraction', - 'fann_set_cascade_output_stagnation_epochs', - 'fann_set_cascade_weight_multiplier', - 'fann_set_error_log', - 'fann_set_input_scaling_params', - 'fann_set_learning_momentum', - 'fann_set_learning_rate', - 'fann_set_output_scaling_params', - 'fann_set_quickprop_decay', - 'fann_set_quickprop_mu', - 'fann_set_rprop_decrease_factor', - 'fann_set_rprop_delta_max', - 'fann_set_rprop_delta_min', - 'fann_set_rprop_delta_zero', - 'fann_set_rprop_increase_factor', - 'fann_set_sarprop_step_error_shift', - 'fann_set_sarprop_step_error_threshold_factor', - 'fann_set_sarprop_temperature', - 'fann_set_sarprop_weight_decay_shift', - 'fann_set_scaling_params', - 'fann_set_train_error_function', - 'fann_set_train_stop_function', - 'fann_set_training_algorithm', - 'fann_set_weight', - 'fann_set_weight_array', - 'fann_shuffle_train_data', - 'fann_subset_train_data', - 'fann_test', - 'fann_test_data', - 'fann_train', - 'fann_train_epoch', - 'fann_train_on_data', - 'fann_train_on_file', - 'fbsql_affected_rows', - 'fbsql_autocommit', - 'fbsql_blob_size', - 'fbsql_change_user', - 'fbsql_clob_size', - 'fbsql_close', - 'fbsql_commit', - 'fbsql_connect', - 'fbsql_create_blob', - 'fbsql_create_clob', - 'fbsql_create_db', - 'fbsql_data_seek', - 'fbsql_database', - 'fbsql_database_password', - 'fbsql_db_query', - 'fbsql_db_status', - 'fbsql_drop_db', - 'fbsql_errno', - 'fbsql_error', - 'fbsql_fetch_array', - 'fbsql_fetch_assoc', - 'fbsql_fetch_field', - 'fbsql_fetch_lengths', - 'fbsql_fetch_object', - 'fbsql_fetch_row', - 'fbsql_field_flags', - 'fbsql_field_len', - 'fbsql_field_name', - 'fbsql_field_seek', - 'fbsql_field_table', - 'fbsql_field_type', - 'fbsql_free_result', - 'fbsql_get_autostart_info', - 'fbsql_hostname', - 'fbsql_insert_id', - 'fbsql_list_dbs', - 'fbsql_list_fields', - 'fbsql_list_tables', - 'fbsql_next_result', - 'fbsql_num_fields', - 'fbsql_num_rows', - 'fbsql_password', - 'fbsql_pconnect', - 'fbsql_query', - 'fbsql_read_blob', - 'fbsql_read_clob', - 'fbsql_result', - 'fbsql_rollback', - 'fbsql_rows_fetched', - 'fbsql_select_db', - 'fbsql_set_characterset', - 'fbsql_set_lob_mode', - 'fbsql_set_password', - 'fbsql_set_transaction', - 'fbsql_start_db', - 'fbsql_stop_db', - 'fbsql_table_name', - 'fbsql_username', - 'fclose', - 'fdf_add_doc_javascript', - 'fdf_add_template', - 'fdf_close', - 'fdf_create', - 'fdf_enum_values', - 'fdf_get_ap', - 'fdf_get_attachment', - 'fdf_get_encoding', - 'fdf_get_file', - 'fdf_get_flags', - 'fdf_get_opt', - 'fdf_get_status', - 'fdf_get_value', - 'fdf_get_version', - 'fdf_next_field_name', - 'fdf_open', - 'fdf_open_string', - 'fdf_remove_item', - 'fdf_save', - 'fdf_save_string', - 'fdf_set_ap', - 'fdf_set_encoding', - 'fdf_set_file', - 'fdf_set_flags', - 'fdf_set_javascript_action', - 'fdf_set_on_import_javascript', - 'fdf_set_opt', - 'fdf_set_status', - 'fdf_set_submit_form_action', - 'fdf_set_target_frame', - 'fdf_set_value', - 'fdf_set_version', - 'feof', - 'fflush', - 'ffmpeg_frame::__construct', - 'ffmpeg_frame::toGDImage', - 'fgetc', - 'fgetcsv', - 'fgets', - 'fgetss', - 'file', - 'file_get_contents', - 'file_put_contents', - 'finfo::buffer', - 'finfo::file', - 'finfo_buffer', - 'finfo_close', - 'finfo_file', - 'finfo_open', - 'finfo_set_flags', - 'flock', - 'fopen', - 'fpassthru', - 'fprintf', - 'fputcsv', - 'fputs', - 'fread', - 'fscanf', - 'fseek', - 'fstat', - 'ftell', - 'ftp_alloc', - 'ftp_append', - 'ftp_cdup', - 'ftp_chdir', - 'ftp_chmod', - 'ftp_close', - 'ftp_delete', - 'ftp_exec', - 'ftp_fget', - 'ftp_fput', - 'ftp_get', - 'ftp_get_option', - 'ftp_login', - 'ftp_mdtm', - 'ftp_mkdir', - 'ftp_mlsd', - 'ftp_nb_continue', - 'ftp_nb_fget', - 'ftp_nb_fput', - 'ftp_nb_get', - 'ftp_nb_put', - 'ftp_nlist', - 'ftp_pasv', - 'ftp_put', - 'ftp_pwd', - 'ftp_quit', - 'ftp_raw', - 'ftp_rawlist', - 'ftp_rename', - 'ftp_rmdir', - 'ftp_set_option', - 'ftp_site', - 'ftp_size', - 'ftp_systype', - 'ftruncate', - 'fwrite', - 'get_resource_type', - 'gmp_div', - 'gnupg::init', - 'gnupg_adddecryptkey', - 'gnupg_addencryptkey', - 'gnupg_addsignkey', - 'gnupg_cleardecryptkeys', - 'gnupg_clearencryptkeys', - 'gnupg_clearsignkeys', - 'gnupg_decrypt', - 'gnupg_decryptverify', - 'gnupg_encrypt', - 'gnupg_encryptsign', - 'gnupg_export', - 'gnupg_geterror', - 'gnupg_getprotocol', - 'gnupg_import', - 'gnupg_init', - 'gnupg_keyinfo', - 'gnupg_setarmor', - 'gnupg_seterrormode', - 'gnupg_setsignmode', - 'gnupg_sign', - 'gnupg_verify', - 'gupnp_context_get_host_ip', - 'gupnp_context_get_port', - 'gupnp_context_get_subscription_timeout', - 'gupnp_context_host_path', - 'gupnp_context_new', - 'gupnp_context_set_subscription_timeout', - 'gupnp_context_timeout_add', - 'gupnp_context_unhost_path', - 'gupnp_control_point_browse_start', - 'gupnp_control_point_browse_stop', - 'gupnp_control_point_callback_set', - 'gupnp_control_point_new', - 'gupnp_device_action_callback_set', - 'gupnp_device_info_get', - 'gupnp_device_info_get_service', - 'gupnp_root_device_get_available', - 'gupnp_root_device_get_relative_location', - 'gupnp_root_device_new', - 'gupnp_root_device_set_available', - 'gupnp_root_device_start', - 'gupnp_root_device_stop', - 'gupnp_service_action_get', - 'gupnp_service_action_return', - 'gupnp_service_action_return_error', - 'gupnp_service_action_set', - 'gupnp_service_freeze_notify', - 'gupnp_service_info_get', - 'gupnp_service_info_get_introspection', - 'gupnp_service_introspection_get_state_variable', - 'gupnp_service_notify', - 'gupnp_service_proxy_action_get', - 'gupnp_service_proxy_action_set', - 'gupnp_service_proxy_add_notify', - 'gupnp_service_proxy_callback_set', - 'gupnp_service_proxy_get_subscribed', - 'gupnp_service_proxy_remove_notify', - 'gupnp_service_proxy_send_action', - 'gupnp_service_proxy_set_subscribed', - 'gupnp_service_thaw_notify', - 'gzclose', - 'gzeof', - 'gzgetc', - 'gzgets', - 'gzgetss', - 'gzpassthru', - 'gzputs', - 'gzread', - 'gzrewind', - 'gzseek', - 'gztell', - 'gzwrite', - 'hash_update_stream', - 'http\Env\Response::send', - 'http_get_request_body_stream', - 'ibase_add_user', - 'ibase_affected_rows', - 'ibase_backup', - 'ibase_blob_add', - 'ibase_blob_cancel', - 'ibase_blob_close', - 'ibase_blob_create', - 'ibase_blob_get', - 'ibase_blob_open', - 'ibase_close', - 'ibase_commit', - 'ibase_commit_ret', - 'ibase_connect', - 'ibase_db_info', - 'ibase_delete_user', - 'ibase_drop_db', - 'ibase_execute', - 'ibase_fetch_assoc', - 'ibase_fetch_object', - 'ibase_fetch_row', - 'ibase_field_info', - 'ibase_free_event_handler', - 'ibase_free_query', - 'ibase_free_result', - 'ibase_gen_id', - 'ibase_maintain_db', - 'ibase_modify_user', - 'ibase_name_result', - 'ibase_num_fields', - 'ibase_num_params', - 'ibase_param_info', - 'ibase_pconnect', - 'ibase_prepare', - 'ibase_query', - 'ibase_restore', - 'ibase_rollback', - 'ibase_rollback_ret', - 'ibase_server_info', - 'ibase_service_attach', - 'ibase_service_detach', - 'ibase_set_event_handler', - 'ibase_trans', - 'ifx_affected_rows', - 'ifx_close', - 'ifx_connect', - 'ifx_do', - 'ifx_error', - 'ifx_fetch_row', - 'ifx_fieldproperties', - 'ifx_fieldtypes', - 'ifx_free_result', - 'ifx_getsqlca', - 'ifx_htmltbl_result', - 'ifx_num_fields', - 'ifx_num_rows', - 'ifx_pconnect', - 'ifx_prepare', - 'ifx_query', - 'image2wbmp', - 'imageaffine', - 'imagealphablending', - 'imageantialias', - 'imagearc', - 'imagebmp', - 'imagechar', - 'imagecharup', - 'imagecolorallocate', - 'imagecolorallocatealpha', - 'imagecolorat', - 'imagecolorclosest', - 'imagecolorclosestalpha', - 'imagecolorclosesthwb', - 'imagecolordeallocate', - 'imagecolorexact', - 'imagecolorexactalpha', - 'imagecolormatch', - 'imagecolorresolve', - 'imagecolorresolvealpha', - 'imagecolorset', - 'imagecolorsforindex', - 'imagecolorstotal', - 'imagecolortransparent', - 'imageconvolution', - 'imagecopy', - 'imagecopymerge', - 'imagecopymergegray', - 'imagecopyresampled', - 'imagecopyresized', - 'imagecrop', - 'imagecropauto', - 'imagedashedline', - 'imagedestroy', - 'imageellipse', - 'imagefill', - 'imagefilledarc', - 'imagefilledellipse', - 'imagefilledpolygon', - 'imagefilledrectangle', - 'imagefilltoborder', - 'imagefilter', - 'imageflip', - 'imagefttext', - 'imagegammacorrect', - 'imagegd', - 'imagegd2', - 'imagegetclip', - 'imagegif', - 'imagegrabscreen', - 'imagegrabwindow', - 'imageinterlace', - 'imageistruecolor', - 'imagejpeg', - 'imagelayereffect', - 'imageline', - 'imageopenpolygon', - 'imagepalettecopy', - 'imagepalettetotruecolor', - 'imagepng', - 'imagepolygon', - 'imagepsencodefont', - 'imagepsextendfont', - 'imagepsfreefont', - 'imagepsloadfont', - 'imagepsslantfont', - 'imagepstext', - 'imagerectangle', - 'imageresolution', - 'imagerotate', - 'imagesavealpha', - 'imagescale', - 'imagesetbrush', - 'imagesetclip', - 'imagesetinterpolation', - 'imagesetpixel', - 'imagesetstyle', - 'imagesetthickness', - 'imagesettile', - 'imagestring', - 'imagestringup', - 'imagesx', - 'imagesy', - 'imagetruecolortopalette', - 'imagettftext', - 'imagewbmp', - 'imagewebp', - 'imagexbm', - 'imap_append', - 'imap_body', - 'imap_bodystruct', - 'imap_check', - 'imap_clearflag_full', - 'imap_close', - 'imap_create', - 'imap_createmailbox', - 'imap_delete', - 'imap_deletemailbox', - 'imap_expunge', - 'imap_fetch_overview', - 'imap_fetchbody', - 'imap_fetchheader', - 'imap_fetchmime', - 'imap_fetchstructure', - 'imap_fetchtext', - 'imap_gc', - 'imap_get_quota', - 'imap_get_quotaroot', - 'imap_getacl', - 'imap_getmailboxes', - 'imap_getsubscribed', - 'imap_header', - 'imap_headerinfo', - 'imap_headers', - 'imap_list', - 'imap_listmailbox', - 'imap_listscan', - 'imap_listsubscribed', - 'imap_lsub', - 'imap_mail_copy', - 'imap_mail_move', - 'imap_mailboxmsginfo', - 'imap_msgno', - 'imap_num_msg', - 'imap_num_recent', - 'imap_ping', - 'imap_rename', - 'imap_renamemailbox', - 'imap_reopen', - 'imap_savebody', - 'imap_scan', - 'imap_scanmailbox', - 'imap_search', - 'imap_set_quota', - 'imap_setacl', - 'imap_setflag_full', - 'imap_sort', - 'imap_status', - 'imap_subscribe', - 'imap_thread', - 'imap_uid', - 'imap_undelete', - 'imap_unsubscribe', - 'inflate_add', - 'inflate_get_read_len', - 'inflate_get_status', - 'ingres_autocommit', - 'ingres_autocommit_state', - 'ingres_charset', - 'ingres_close', - 'ingres_commit', - 'ingres_connect', - 'ingres_cursor', - 'ingres_errno', - 'ingres_error', - 'ingres_errsqlstate', - 'ingres_escape_string', - 'ingres_execute', - 'ingres_fetch_array', - 'ingres_fetch_assoc', - 'ingres_fetch_object', - 'ingres_fetch_proc_return', - 'ingres_fetch_row', - 'ingres_field_length', - 'ingres_field_name', - 'ingres_field_nullable', - 'ingres_field_precision', - 'ingres_field_scale', - 'ingres_field_type', - 'ingres_free_result', - 'ingres_next_error', - 'ingres_num_fields', - 'ingres_num_rows', - 'ingres_pconnect', - 'ingres_prepare', - 'ingres_query', - 'ingres_result_seek', - 'ingres_rollback', - 'ingres_set_environment', - 'ingres_unbuffered_query', - 'inotify_add_watch', - 'inotify_init', - 'inotify_queue_len', - 'inotify_read', - 'inotify_rm_watch', - 'kadm5_chpass_principal', - 'kadm5_create_principal', - 'kadm5_delete_principal', - 'kadm5_destroy', - 'kadm5_flush', - 'kadm5_get_policies', - 'kadm5_get_principal', - 'kadm5_get_principals', - 'kadm5_init_with_password', - 'kadm5_modify_principal', - 'ldap_add', - 'ldap_bind', - 'ldap_close', - 'ldap_compare', - 'ldap_control_paged_result', - 'ldap_control_paged_result_response', - 'ldap_count_entries', - 'ldap_delete', - 'ldap_errno', - 'ldap_error', - 'ldap_exop', - 'ldap_exop_passwd', - 'ldap_exop_refresh', - 'ldap_exop_whoami', - 'ldap_first_attribute', - 'ldap_first_entry', - 'ldap_first_reference', - 'ldap_free_result', - 'ldap_get_attributes', - 'ldap_get_dn', - 'ldap_get_entries', - 'ldap_get_option', - 'ldap_get_values', - 'ldap_get_values_len', - 'ldap_mod_add', - 'ldap_mod_del', - 'ldap_mod_replace', - 'ldap_modify', - 'ldap_modify_batch', - 'ldap_next_attribute', - 'ldap_next_entry', - 'ldap_next_reference', - 'ldap_parse_exop', - 'ldap_parse_reference', - 'ldap_parse_result', - 'ldap_rename', - 'ldap_sasl_bind', - 'ldap_set_option', - 'ldap_set_rebind_proc', - 'ldap_sort', - 'ldap_start_tls', - 'ldap_unbind', - 'libxml_set_streams_context', - 'm_checkstatus', - 'm_completeauthorizations', - 'm_connect', - 'm_connectionerror', - 'm_deletetrans', - 'm_destroyconn', - 'm_getcell', - 'm_getcellbynum', - 'm_getcommadelimited', - 'm_getheader', - 'm_initconn', - 'm_iscommadelimited', - 'm_maxconntimeout', - 'm_monitor', - 'm_numcolumns', - 'm_numrows', - 'm_parsecommadelimited', - 'm_responsekeys', - 'm_responseparam', - 'm_returnstatus', - 'm_setblocking', - 'm_setdropfile', - 'm_setip', - 'm_setssl', - 'm_setssl_cafile', - 'm_setssl_files', - 'm_settimeout', - 'm_transactionssent', - 'm_transinqueue', - 'm_transkeyval', - 'm_transnew', - 'm_transsend', - 'm_validateidentifier', - 'm_verifyconnection', - 'm_verifysslcert', - 'mailparse_determine_best_xfer_encoding', - 'mailparse_msg_create', - 'mailparse_msg_extract_part', - 'mailparse_msg_extract_part_file', - 'mailparse_msg_extract_whole_part_file', - 'mailparse_msg_free', - 'mailparse_msg_get_part', - 'mailparse_msg_get_part_data', - 'mailparse_msg_get_structure', - 'mailparse_msg_parse', - 'mailparse_msg_parse_file', - 'mailparse_stream_encode', - 'mailparse_uudecode_all', - 'maxdb::use_result', - 'maxdb_affected_rows', - 'maxdb_connect', - 'maxdb_disable_rpl_parse', - 'maxdb_dump_debug_info', - 'maxdb_embedded_connect', - 'maxdb_enable_reads_from_master', - 'maxdb_enable_rpl_parse', - 'maxdb_errno', - 'maxdb_error', - 'maxdb_fetch_lengths', - 'maxdb_field_tell', - 'maxdb_get_host_info', - 'maxdb_get_proto_info', - 'maxdb_get_server_info', - 'maxdb_get_server_version', - 'maxdb_info', - 'maxdb_init', - 'maxdb_insert_id', - 'maxdb_master_query', - 'maxdb_more_results', - 'maxdb_next_result', - 'maxdb_num_fields', - 'maxdb_num_rows', - 'maxdb_rpl_parse_enabled', - 'maxdb_rpl_probe', - 'maxdb_select_db', - 'maxdb_sqlstate', - 'maxdb_stmt::result_metadata', - 'maxdb_stmt_affected_rows', - 'maxdb_stmt_errno', - 'maxdb_stmt_error', - 'maxdb_stmt_num_rows', - 'maxdb_stmt_param_count', - 'maxdb_stmt_result_metadata', - 'maxdb_stmt_sqlstate', - 'maxdb_thread_id', - 'maxdb_use_result', - 'maxdb_warning_count', - 'mcrypt_enc_get_algorithms_name', - 'mcrypt_enc_get_block_size', - 'mcrypt_enc_get_iv_size', - 'mcrypt_enc_get_key_size', - 'mcrypt_enc_get_modes_name', - 'mcrypt_enc_get_supported_key_sizes', - 'mcrypt_enc_is_block_algorithm', - 'mcrypt_enc_is_block_algorithm_mode', - 'mcrypt_enc_is_block_mode', - 'mcrypt_enc_self_test', - 'mcrypt_generic', - 'mcrypt_generic_deinit', - 'mcrypt_generic_end', - 'mcrypt_generic_init', - 'mcrypt_module_close', - 'mcrypt_module_open', - 'mdecrypt_generic', - 'mkdir', - 'mqseries_back', - 'mqseries_begin', - 'mqseries_close', - 'mqseries_cmit', - 'mqseries_conn', - 'mqseries_connx', - 'mqseries_disc', - 'mqseries_get', - 'mqseries_inq', - 'mqseries_open', - 'mqseries_put', - 'mqseries_put1', - 'mqseries_set', - 'msg_get_queue', - 'msg_receive', - 'msg_remove_queue', - 'msg_send', - 'msg_set_queue', - 'msg_stat_queue', - 'msql_affected_rows', - 'msql_close', - 'msql_connect', - 'msql_create_db', - 'msql_data_seek', - 'msql_db_query', - 'msql_drop_db', - 'msql_fetch_array', - 'msql_fetch_field', - 'msql_fetch_object', - 'msql_fetch_row', - 'msql_field_flags', - 'msql_field_len', - 'msql_field_name', - 'msql_field_seek', - 'msql_field_table', - 'msql_field_type', - 'msql_free_result', - 'msql_list_dbs', - 'msql_list_fields', - 'msql_list_tables', - 'msql_num_fields', - 'msql_num_rows', - 'msql_pconnect', - 'msql_query', - 'msql_result', - 'msql_select_db', - 'mssql_bind', - 'mssql_close', - 'mssql_connect', - 'mssql_data_seek', - 'mssql_execute', - 'mssql_fetch_array', - 'mssql_fetch_assoc', - 'mssql_fetch_batch', - 'mssql_fetch_field', - 'mssql_fetch_object', - 'mssql_fetch_row', - 'mssql_field_length', - 'mssql_field_name', - 'mssql_field_seek', - 'mssql_field_type', - 'mssql_free_result', - 'mssql_free_statement', - 'mssql_init', - 'mssql_next_result', - 'mssql_num_fields', - 'mssql_num_rows', - 'mssql_pconnect', - 'mssql_query', - 'mssql_result', - 'mssql_rows_affected', - 'mssql_select_db', - 'mysql_affected_rows', - 'mysql_client_encoding', - 'mysql_close', - 'mysql_connect', - 'mysql_create_db', - 'mysql_data_seek', - 'mysql_db_name', - 'mysql_db_query', - 'mysql_drop_db', - 'mysql_errno', - 'mysql_error', - 'mysql_fetch_array', - 'mysql_fetch_assoc', - 'mysql_fetch_field', - 'mysql_fetch_lengths', - 'mysql_fetch_object', - 'mysql_fetch_row', - 'mysql_field_flags', - 'mysql_field_len', - 'mysql_field_name', - 'mysql_field_seek', - 'mysql_field_table', - 'mysql_field_type', - 'mysql_free_result', - 'mysql_get_host_info', - 'mysql_get_proto_info', - 'mysql_get_server_info', - 'mysql_info', - 'mysql_insert_id', - 'mysql_list_dbs', - 'mysql_list_fields', - 'mysql_list_processes', - 'mysql_list_tables', - 'mysql_num_fields', - 'mysql_num_rows', - 'mysql_pconnect', - 'mysql_ping', - 'mysql_query', - 'mysql_real_escape_string', - 'mysql_result', - 'mysql_select_db', - 'mysql_set_charset', - 'mysql_stat', - 'mysql_tablename', - 'mysql_thread_id', - 'mysql_unbuffered_query', - 'mysqlnd_uh_convert_to_mysqlnd', - 'ncurses_bottom_panel', - 'ncurses_del_panel', - 'ncurses_delwin', - 'ncurses_getmaxyx', - 'ncurses_getyx', - 'ncurses_hide_panel', - 'ncurses_keypad', - 'ncurses_meta', - 'ncurses_move_panel', - 'ncurses_mvwaddstr', - 'ncurses_new_panel', - 'ncurses_newpad', - 'ncurses_newwin', - 'ncurses_panel_above', - 'ncurses_panel_below', - 'ncurses_panel_window', - 'ncurses_pnoutrefresh', - 'ncurses_prefresh', - 'ncurses_replace_panel', - 'ncurses_show_panel', - 'ncurses_top_panel', - 'ncurses_waddch', - 'ncurses_waddstr', - 'ncurses_wattroff', - 'ncurses_wattron', - 'ncurses_wattrset', - 'ncurses_wborder', - 'ncurses_wclear', - 'ncurses_wcolor_set', - 'ncurses_werase', - 'ncurses_wgetch', - 'ncurses_whline', - 'ncurses_wmouse_trafo', - 'ncurses_wmove', - 'ncurses_wnoutrefresh', - 'ncurses_wrefresh', - 'ncurses_wstandend', - 'ncurses_wstandout', - 'ncurses_wvline', - 'newt_button', - 'newt_button_bar', - 'newt_checkbox', - 'newt_checkbox_get_value', - 'newt_checkbox_set_flags', - 'newt_checkbox_set_value', - 'newt_checkbox_tree', - 'newt_checkbox_tree_add_item', - 'newt_checkbox_tree_find_item', - 'newt_checkbox_tree_get_current', - 'newt_checkbox_tree_get_entry_value', - 'newt_checkbox_tree_get_multi_selection', - 'newt_checkbox_tree_get_selection', - 'newt_checkbox_tree_multi', - 'newt_checkbox_tree_set_current', - 'newt_checkbox_tree_set_entry', - 'newt_checkbox_tree_set_entry_value', - 'newt_checkbox_tree_set_width', - 'newt_compact_button', - 'newt_component_add_callback', - 'newt_component_takes_focus', - 'newt_create_grid', - 'newt_draw_form', - 'newt_entry', - 'newt_entry_get_value', - 'newt_entry_set', - 'newt_entry_set_filter', - 'newt_entry_set_flags', - 'newt_form', - 'newt_form_add_component', - 'newt_form_add_components', - 'newt_form_add_hot_key', - 'newt_form_destroy', - 'newt_form_get_current', - 'newt_form_run', - 'newt_form_set_background', - 'newt_form_set_height', - 'newt_form_set_size', - 'newt_form_set_timer', - 'newt_form_set_width', - 'newt_form_watch_fd', - 'newt_grid_add_components_to_form', - 'newt_grid_basic_window', - 'newt_grid_free', - 'newt_grid_get_size', - 'newt_grid_h_close_stacked', - 'newt_grid_h_stacked', - 'newt_grid_place', - 'newt_grid_set_field', - 'newt_grid_simple_window', - 'newt_grid_v_close_stacked', - 'newt_grid_v_stacked', - 'newt_grid_wrapped_window', - 'newt_grid_wrapped_window_at', - 'newt_label', - 'newt_label_set_text', - 'newt_listbox', - 'newt_listbox_append_entry', - 'newt_listbox_clear', - 'newt_listbox_clear_selection', - 'newt_listbox_delete_entry', - 'newt_listbox_get_current', - 'newt_listbox_get_selection', - 'newt_listbox_insert_entry', - 'newt_listbox_item_count', - 'newt_listbox_select_item', - 'newt_listbox_set_current', - 'newt_listbox_set_current_by_key', - 'newt_listbox_set_data', - 'newt_listbox_set_entry', - 'newt_listbox_set_width', - 'newt_listitem', - 'newt_listitem_get_data', - 'newt_listitem_set', - 'newt_radio_get_current', - 'newt_radiobutton', - 'newt_run_form', - 'newt_scale', - 'newt_scale_set', - 'newt_scrollbar_set', - 'newt_textbox', - 'newt_textbox_get_num_lines', - 'newt_textbox_reflowed', - 'newt_textbox_set_height', - 'newt_textbox_set_text', - 'newt_vertical_scrollbar', - 'oci_bind_array_by_name', - 'oci_bind_by_name', - 'oci_cancel', - 'oci_close', - 'oci_commit', - 'oci_connect', - 'oci_define_by_name', - 'oci_error', - 'oci_execute', - 'oci_fetch', - 'oci_fetch_all', - 'oci_fetch_array', - 'oci_fetch_assoc', - 'oci_fetch_object', - 'oci_fetch_row', - 'oci_field_is_null', - 'oci_field_name', - 'oci_field_precision', - 'oci_field_scale', - 'oci_field_size', - 'oci_field_type', - 'oci_field_type_raw', - 'oci_free_cursor', - 'oci_free_statement', - 'oci_get_implicit_resultset', - 'oci_new_collection', - 'oci_new_connect', - 'oci_new_cursor', - 'oci_new_descriptor', - 'oci_num_fields', - 'oci_num_rows', - 'oci_parse', - 'oci_pconnect', - 'oci_register_taf_callback', - 'oci_result', - 'oci_rollback', - 'oci_server_version', - 'oci_set_action', - 'oci_set_client_identifier', - 'oci_set_client_info', - 'oci_set_module_name', - 'oci_set_prefetch', - 'oci_statement_type', - 'oci_unregister_taf_callback', - 'odbc_autocommit', - 'odbc_close', - 'odbc_columnprivileges', - 'odbc_columns', - 'odbc_commit', - 'odbc_connect', - 'odbc_cursor', - 'odbc_data_source', - 'odbc_do', - 'odbc_error', - 'odbc_errormsg', - 'odbc_exec', - 'odbc_execute', - 'odbc_fetch_array', - 'odbc_fetch_into', - 'odbc_fetch_row', - 'odbc_field_len', - 'odbc_field_name', - 'odbc_field_num', - 'odbc_field_precision', - 'odbc_field_scale', - 'odbc_field_type', - 'odbc_foreignkeys', - 'odbc_free_result', - 'odbc_gettypeinfo', - 'odbc_next_result', - 'odbc_num_fields', - 'odbc_num_rows', - 'odbc_pconnect', - 'odbc_prepare', - 'odbc_primarykeys', - 'odbc_procedurecolumns', - 'odbc_procedures', - 'odbc_result', - 'odbc_result_all', - 'odbc_rollback', - 'odbc_setoption', - 'odbc_specialcolumns', - 'odbc_statistics', - 'odbc_tableprivileges', - 'odbc_tables', - 'openal_buffer_create', - 'openal_buffer_data', - 'openal_buffer_destroy', - 'openal_buffer_get', - 'openal_buffer_loadwav', - 'openal_context_create', - 'openal_context_current', - 'openal_context_destroy', - 'openal_context_process', - 'openal_context_suspend', - 'openal_device_close', - 'openal_device_open', - 'openal_source_create', - 'openal_source_destroy', - 'openal_source_get', - 'openal_source_pause', - 'openal_source_play', - 'openal_source_rewind', - 'openal_source_set', - 'openal_source_stop', - 'openal_stream', - 'opendir', - 'openssl_csr_new', - 'openssl_dh_compute_key', - 'openssl_free_key', - 'openssl_pkey_export', - 'openssl_pkey_free', - 'openssl_pkey_get_details', - 'openssl_spki_new', - 'openssl_x509_free', - 'pclose', - 'pfsockopen', - 'pg_affected_rows', - 'pg_cancel_query', - 'pg_client_encoding', - 'pg_close', - 'pg_connect_poll', - 'pg_connection_busy', - 'pg_connection_reset', - 'pg_connection_status', - 'pg_consume_input', - 'pg_convert', - 'pg_copy_from', - 'pg_copy_to', - 'pg_dbname', - 'pg_delete', - 'pg_end_copy', - 'pg_escape_bytea', - 'pg_escape_identifier', - 'pg_escape_literal', - 'pg_escape_string', - 'pg_execute', - 'pg_fetch_all', - 'pg_fetch_all_columns', - 'pg_fetch_array', - 'pg_fetch_assoc', - 'pg_fetch_row', - 'pg_field_name', - 'pg_field_num', - 'pg_field_size', - 'pg_field_table', - 'pg_field_type', - 'pg_field_type_oid', - 'pg_flush', - 'pg_free_result', - 'pg_get_notify', - 'pg_get_pid', - 'pg_get_result', - 'pg_host', - 'pg_insert', - 'pg_last_error', - 'pg_last_notice', - 'pg_last_oid', - 'pg_lo_close', - 'pg_lo_create', - 'pg_lo_export', - 'pg_lo_import', - 'pg_lo_open', - 'pg_lo_read', - 'pg_lo_read_all', - 'pg_lo_seek', - 'pg_lo_tell', - 'pg_lo_truncate', - 'pg_lo_unlink', - 'pg_lo_write', - 'pg_meta_data', - 'pg_num_fields', - 'pg_num_rows', - 'pg_options', - 'pg_parameter_status', - 'pg_ping', - 'pg_port', - 'pg_prepare', - 'pg_put_line', - 'pg_query', - 'pg_query_params', - 'pg_result_error', - 'pg_result_error_field', - 'pg_result_seek', - 'pg_result_status', - 'pg_select', - 'pg_send_execute', - 'pg_send_prepare', - 'pg_send_query', - 'pg_send_query_params', - 'pg_set_client_encoding', - 'pg_set_error_verbosity', - 'pg_socket', - 'pg_trace', - 'pg_transaction_status', - 'pg_tty', - 'pg_untrace', - 'pg_update', - 'pg_version', - 'php_user_filter::filter', - 'proc_close', - 'proc_get_status', - 'proc_terminate', - 'ps_add_bookmark', - 'ps_add_launchlink', - 'ps_add_locallink', - 'ps_add_note', - 'ps_add_pdflink', - 'ps_add_weblink', - 'ps_arc', - 'ps_arcn', - 'ps_begin_page', - 'ps_begin_pattern', - 'ps_begin_template', - 'ps_circle', - 'ps_clip', - 'ps_close', - 'ps_close_image', - 'ps_closepath', - 'ps_closepath_stroke', - 'ps_continue_text', - 'ps_curveto', - 'ps_delete', - 'ps_end_page', - 'ps_end_pattern', - 'ps_end_template', - 'ps_fill', - 'ps_fill_stroke', - 'ps_findfont', - 'ps_get_buffer', - 'ps_get_parameter', - 'ps_get_value', - 'ps_hyphenate', - 'ps_include_file', - 'ps_lineto', - 'ps_makespotcolor', - 'ps_moveto', - 'ps_new', - 'ps_open_file', - 'ps_open_image', - 'ps_open_image_file', - 'ps_open_memory_image', - 'ps_place_image', - 'ps_rect', - 'ps_restore', - 'ps_rotate', - 'ps_save', - 'ps_scale', - 'ps_set_border_color', - 'ps_set_border_dash', - 'ps_set_border_style', - 'ps_set_info', - 'ps_set_parameter', - 'ps_set_text_pos', - 'ps_set_value', - 'ps_setcolor', - 'ps_setdash', - 'ps_setflat', - 'ps_setfont', - 'ps_setgray', - 'ps_setlinecap', - 'ps_setlinejoin', - 'ps_setlinewidth', - 'ps_setmiterlimit', - 'ps_setoverprintmode', - 'ps_setpolydash', - 'ps_shading', - 'ps_shading_pattern', - 'ps_shfill', - 'ps_show', - 'ps_show2', - 'ps_show_boxed', - 'ps_show_xy', - 'ps_show_xy2', - 'ps_string_geometry', - 'ps_stringwidth', - 'ps_stroke', - 'ps_symbol', - 'ps_symbol_name', - 'ps_symbol_width', - 'ps_translate', - 'px_close', - 'px_create_fp', - 'px_date2string', - 'px_delete', - 'px_delete_record', - 'px_get_field', - 'px_get_info', - 'px_get_parameter', - 'px_get_record', - 'px_get_schema', - 'px_get_value', - 'px_insert_record', - 'px_new', - 'px_numfields', - 'px_numrecords', - 'px_open_fp', - 'px_put_record', - 'px_retrieve_record', - 'px_set_blob_file', - 'px_set_parameter', - 'px_set_tablename', - 'px_set_targetencoding', - 'px_set_value', - 'px_timestamp2string', - 'px_update_record', - 'radius_acct_open', - 'radius_add_server', - 'radius_auth_open', - 'radius_close', - 'radius_config', - 'radius_create_request', - 'radius_demangle', - 'radius_demangle_mppe_key', - 'radius_get_attr', - 'radius_put_addr', - 'radius_put_attr', - 'radius_put_int', - 'radius_put_string', - 'radius_put_vendor_addr', - 'radius_put_vendor_attr', - 'radius_put_vendor_int', - 'radius_put_vendor_string', - 'radius_request_authenticator', - 'radius_salt_encrypt_attr', - 'radius_send_request', - 'radius_server_secret', - 'radius_strerror', - 'readdir', - 'readfile', - 'recode_file', - 'rename', - 'rewind', - 'rewinddir', - 'rmdir', - 'rpm_close', - 'rpm_get_tag', - 'rpm_open', - 'sapi_windows_vt100_support', - 'scandir', - 'sem_acquire', - 'sem_get', - 'sem_release', - 'sem_remove', - 'set_file_buffer', - 'shm_attach', - 'shm_detach', - 'shm_get_var', - 'shm_has_var', - 'shm_put_var', - 'shm_remove', - 'shm_remove_var', - 'shmop_close', - 'shmop_delete', - 'shmop_open', - 'shmop_read', - 'shmop_size', - 'shmop_write', - 'socket_accept', - 'socket_addrinfo_bind', - 'socket_addrinfo_connect', - 'socket_addrinfo_explain', - 'socket_bind', - 'socket_clear_error', - 'socket_close', - 'socket_connect', - 'socket_export_stream', - 'socket_get_option', - 'socket_get_status', - 'socket_getopt', - 'socket_getpeername', - 'socket_getsockname', - 'socket_import_stream', - 'socket_last_error', - 'socket_listen', - 'socket_read', - 'socket_recv', - 'socket_recvfrom', - 'socket_recvmsg', - 'socket_send', - 'socket_sendmsg', - 'socket_sendto', - 'socket_set_block', - 'socket_set_blocking', - 'socket_set_nonblock', - 'socket_set_option', - 'socket_set_timeout', - 'socket_shutdown', - 'socket_write', - 'sqlite_close', - 'sqlite_fetch_string', - 'sqlite_has_more', - 'sqlite_open', - 'sqlite_popen', - 'sqlsrv_begin_transaction', - 'sqlsrv_cancel', - 'sqlsrv_client_info', - 'sqlsrv_close', - 'sqlsrv_commit', - 'sqlsrv_connect', - 'sqlsrv_execute', - 'sqlsrv_fetch', - 'sqlsrv_fetch_array', - 'sqlsrv_fetch_object', - 'sqlsrv_field_metadata', - 'sqlsrv_free_stmt', - 'sqlsrv_get_field', - 'sqlsrv_has_rows', - 'sqlsrv_next_result', - 'sqlsrv_num_fields', - 'sqlsrv_num_rows', - 'sqlsrv_prepare', - 'sqlsrv_query', - 'sqlsrv_rollback', - 'sqlsrv_rows_affected', - 'sqlsrv_send_stream_data', - 'sqlsrv_server_info', - 'ssh2_auth_agent', - 'ssh2_auth_hostbased_file', - 'ssh2_auth_none', - 'ssh2_auth_password', - 'ssh2_auth_pubkey_file', - 'ssh2_disconnect', - 'ssh2_exec', - 'ssh2_fetch_stream', - 'ssh2_fingerprint', - 'ssh2_methods_negotiated', - 'ssh2_publickey_add', - 'ssh2_publickey_init', - 'ssh2_publickey_list', - 'ssh2_publickey_remove', - 'ssh2_scp_recv', - 'ssh2_scp_send', - 'ssh2_sftp', - 'ssh2_sftp_chmod', - 'ssh2_sftp_lstat', - 'ssh2_sftp_mkdir', - 'ssh2_sftp_readlink', - 'ssh2_sftp_realpath', - 'ssh2_sftp_rename', - 'ssh2_sftp_rmdir', - 'ssh2_sftp_stat', - 'ssh2_sftp_symlink', - 'ssh2_sftp_unlink', - 'ssh2_shell', - 'ssh2_tunnel', - 'stomp_connect', - 'streamWrapper::stream_cast', - 'stream_bucket_append', - 'stream_bucket_make_writeable', - 'stream_bucket_new', - 'stream_bucket_prepend', - 'stream_context_create', - 'stream_context_get_default', - 'stream_context_get_options', - 'stream_context_get_params', - 'stream_context_set_default', - 'stream_context_set_params', - 'stream_copy_to_stream', - 'stream_encoding', - 'stream_filter_append', - 'stream_filter_prepend', - 'stream_filter_remove', - 'stream_get_contents', - 'stream_get_line', - 'stream_get_meta_data', - 'stream_isatty', - 'stream_set_blocking', - 'stream_set_chunk_size', - 'stream_set_read_buffer', - 'stream_set_timeout', - 'stream_set_write_buffer', - 'stream_socket_accept', - 'stream_socket_client', - 'stream_socket_enable_crypto', - 'stream_socket_get_name', - 'stream_socket_recvfrom', - 'stream_socket_sendto', - 'stream_socket_server', - 'stream_socket_shutdown', - 'stream_supports_lock', - 'svn_fs_abort_txn', - 'svn_fs_apply_text', - 'svn_fs_begin_txn2', - 'svn_fs_change_node_prop', - 'svn_fs_check_path', - 'svn_fs_contents_changed', - 'svn_fs_copy', - 'svn_fs_delete', - 'svn_fs_dir_entries', - 'svn_fs_file_contents', - 'svn_fs_file_length', - 'svn_fs_is_dir', - 'svn_fs_is_file', - 'svn_fs_make_dir', - 'svn_fs_make_file', - 'svn_fs_node_created_rev', - 'svn_fs_node_prop', - 'svn_fs_props_changed', - 'svn_fs_revision_prop', - 'svn_fs_revision_root', - 'svn_fs_txn_root', - 'svn_fs_youngest_rev', - 'svn_repos_create', - 'svn_repos_fs', - 'svn_repos_fs_begin_txn_for_commit', - 'svn_repos_fs_commit_txn', - 'svn_repos_open', - 'sybase_affected_rows', - 'sybase_close', - 'sybase_connect', - 'sybase_data_seek', - 'sybase_fetch_array', - 'sybase_fetch_assoc', - 'sybase_fetch_field', - 'sybase_fetch_object', - 'sybase_fetch_row', - 'sybase_field_seek', - 'sybase_free_result', - 'sybase_num_fields', - 'sybase_num_rows', - 'sybase_pconnect', - 'sybase_query', - 'sybase_result', - 'sybase_select_db', - 'sybase_set_message_handler', - 'sybase_unbuffered_query', - 'tmpfile', - 'udm_add_search_limit', - 'udm_alloc_agent', - 'udm_alloc_agent_array', - 'udm_cat_list', - 'udm_cat_path', - 'udm_check_charset', - 'udm_clear_search_limits', - 'udm_crc32', - 'udm_errno', - 'udm_error', - 'udm_find', - 'udm_free_agent', - 'udm_free_res', - 'udm_get_doc_count', - 'udm_get_res_field', - 'udm_get_res_param', - 'udm_hash32', - 'udm_load_ispell_data', - 'udm_set_agent_param', - 'unlink', - 'vfprintf', - 'w32api_init_dtype', - 'wddx_add_vars', - 'wddx_packet_end', - 'wddx_packet_start', - 'xml_get_current_byte_index', - 'xml_get_current_column_number', - 'xml_get_current_line_number', - 'xml_get_error_code', - 'xml_parse', - 'xml_parse_into_struct', - 'xml_parser_create', - 'xml_parser_create_ns', - 'xml_parser_free', - 'xml_parser_get_option', - 'xml_parser_set_option', - 'xml_set_character_data_handler', - 'xml_set_default_handler', - 'xml_set_element_handler', - 'xml_set_end_namespace_decl_handler', - 'xml_set_external_entity_ref_handler', - 'xml_set_notation_decl_handler', - 'xml_set_object', - 'xml_set_processing_instruction_handler', - 'xml_set_start_namespace_decl_handler', - 'xml_set_unparsed_entity_decl_handler', - 'xmlrpc_server_add_introspection_data', - 'xmlrpc_server_call_method', - 'xmlrpc_server_create', - 'xmlrpc_server_destroy', - 'xmlrpc_server_register_introspection_callback', - 'xmlrpc_server_register_method', - 'xmlwriter_end_attribute', - 'xmlwriter_end_cdata', - 'xmlwriter_end_comment', - 'xmlwriter_end_document', - 'xmlwriter_end_dtd', - 'xmlwriter_end_dtd_attlist', - 'xmlwriter_end_dtd_element', - 'xmlwriter_end_dtd_entity', - 'xmlwriter_end_element', - 'xmlwriter_end_pi', - 'xmlwriter_flush', - 'xmlwriter_full_end_element', - 'xmlwriter_open_memory', - 'xmlwriter_open_uri', - 'xmlwriter_output_memory', - 'xmlwriter_set_indent', - 'xmlwriter_set_indent_string', - 'xmlwriter_start_attribute', - 'xmlwriter_start_attribute_ns', - 'xmlwriter_start_cdata', - 'xmlwriter_start_comment', - 'xmlwriter_start_document', - 'xmlwriter_start_dtd', - 'xmlwriter_start_dtd_attlist', - 'xmlwriter_start_dtd_element', - 'xmlwriter_start_dtd_entity', - 'xmlwriter_start_element', - 'xmlwriter_start_element_ns', - 'xmlwriter_start_pi', - 'xmlwriter_text', - 'xmlwriter_write_attribute', - 'xmlwriter_write_attribute_ns', - 'xmlwriter_write_cdata', - 'xmlwriter_write_comment', - 'xmlwriter_write_dtd', - 'xmlwriter_write_dtd_attlist', - 'xmlwriter_write_dtd_element', - 'xmlwriter_write_dtd_entity', - 'xmlwriter_write_element', - 'xmlwriter_write_element_ns', - 'xmlwriter_write_pi', - 'xmlwriter_write_raw', - 'xslt_create', - 'yaz_addinfo', - 'yaz_ccl_conf', - 'yaz_ccl_parse', - 'yaz_close', - 'yaz_database', - 'yaz_element', - 'yaz_errno', - 'yaz_error', - 'yaz_es', - 'yaz_es_result', - 'yaz_get_option', - 'yaz_hits', - 'yaz_itemorder', - 'yaz_present', - 'yaz_range', - 'yaz_record', - 'yaz_scan', - 'yaz_scan_result', - 'yaz_schema', - 'yaz_search', - 'yaz_sort', - 'yaz_syntax', - 'zip_close', - 'zip_entry_close', - 'zip_entry_compressedsize', - 'zip_entry_compressionmethod', - 'zip_entry_filesize', - 'zip_entry_name', - 'zip_entry_open', - 'zip_entry_read', - 'zip_open', - 'zip_read', - ]; - } -} -Resource Operations - -Copyright (c) 2015-2018, Sebastian Bergmann . -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - * Neither the name of Sebastian Bergmann nor the names of his - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -��#cW�g3k���� =��GBMB \ No newline at end of file From d8a143cad0a27895eb44f9eb74d89f315fa66fd2 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 13 Apr 2019 17:16:33 +0800 Subject: [PATCH 322/643] Added log demo --- app/Http/Controller/LogController.php | 62 +++++++++++++++++++++++++++ app/bean.php | 5 ++- 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controller/LogController.php diff --git a/app/Http/Controller/LogController.php b/app/Http/Controller/LogController.php new file mode 100644 index 00000000..5b11cd00 --- /dev/null +++ b/app/Http/Controller/LogController.php @@ -0,0 +1,62 @@ + [ 'flushRequest' => false, - 'enable' => false, + 'enable' => true, + 'json' => false, ], 'httpServer' => [ 'class' => \Swoft\Http\Server\HttpServer::class, - 'port' => 88, + 'port' => 18306, 'listener' => [ 'rpc' => \bean('rpcServer') ], From 517921b2db4f377c2f6f8e7b5039fe42043c8f6b Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 13 Apr 2019 22:25:26 +0800 Subject: [PATCH 323/643] Added redis demo --- app/Http/Controller/RedisController.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index 38be5527..1c63a0d2 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -32,4 +32,23 @@ public function str(): array return $data; } + + /** + * Auto release connection + * + * @RequestMapping("release") + * + * @return array + * @throws \Swoft\Redis\Exception\RedisException + */ + public function release(): array + { + \sgo(function () { + Redis::connection(); + }); + + Redis::connection(); + + return ['release']; + } } \ No newline at end of file From 7a384dc015066e55d3d97a2fac7453fcb02a5d79 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 14 Apr 2019 18:16:31 +0800 Subject: [PATCH 324/643] add some example file --- app/Console/Command/DemoCommand.php | 12 +++++ .../Handler/HttpExceptionHandler.php | 8 ++++ app/Helper/Functions.php | 4 +- composer.dev.json | 45 +++++++++++++++++++ {app/Console/Command => runtime}/.keep | 0 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 app/Console/Command/DemoCommand.php create mode 100644 app/Exception/Handler/HttpExceptionHandler.php create mode 100644 composer.dev.json rename {app/Console/Command => runtime}/.keep (100%) diff --git a/app/Console/Command/DemoCommand.php b/app/Console/Command/DemoCommand.php new file mode 100644 index 00000000..4892d96e --- /dev/null +++ b/app/Console/Command/DemoCommand.php @@ -0,0 +1,12 @@ +=7.1", + "ext-pdo": "*", + "ext-json": "*", + "ext-redis": "*", + "swoft/component": "2.0.x-dev as 2.0" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Helper/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + } + }, + "scripts": { + }, + "repositories": [ + { + "type": "composer", + "url": "https://packagist.laravel-china.org/" + }, + { + "type": "path", + "url": "../swoft-component" + } + ] +} diff --git a/app/Console/Command/.keep b/runtime/.keep similarity index 100% rename from app/Console/Command/.keep rename to runtime/.keep From bc6f7231be1202cf07b5757cf3263cddeff7a8d8 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 14 Apr 2019 18:17:19 +0800 Subject: [PATCH 325/643] Add redis demo --- app/Http/Controller/RedisController.php | 46 ++++++++++++++++++++++++- app/bean.php | 2 +- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index 1c63a0d2..41e17a87 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -34,7 +34,7 @@ public function str(): array } /** - * Auto release connection + * Only to use test. The wrong way to use it * * @RequestMapping("release") * @@ -51,4 +51,48 @@ public function release(): array return ['release']; } + + /** + * Only to use test. The wrong way to use it + * + * @RequestMapping("ep") + * + * @return array + */ + public function exPipeline(): array + { + \sgo(function () { + Redis::pipeline(function () { + throw new \Exception(''); + }); + }); + + Redis::pipeline(function () { + throw new \Exception(''); + }); + + return ['exPipeline']; + } + + /** + * Only to use test. The wrong way to use it + * + * @RequestMapping("et") + * + * @return array + */ + public function exTransaction(): array + { + \sgo(function () { + Redis::transaction(function () { + throw new \Exception(''); + }); + }); + + Redis::transaction(function () { + throw new \Exception(''); + }); + + return ['exPipeline']; + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 2f67d22b..58f80bbc 100644 --- a/app/bean.php +++ b/app/bean.php @@ -2,7 +2,7 @@ return [ 'logger' => [ 'flushRequest' => false, - 'enable' => true, + 'enable' => false, 'json' => false, ], 'httpServer' => [ From af3934502408bb771a6477daa45001ff35a3367f Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 14 Apr 2019 18:34:43 +0800 Subject: [PATCH 326/643] Added error component --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 91ef62a5..2a0d91a8 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,6 @@ "php": ">=7.1", "ext-pdo": "*", "ext-json": "*", - "ext-redis": "*", "swoft/annotation": "^2.0", "swoft/bean": "^2.0", "swoft/event": "^2.0", @@ -36,6 +35,7 @@ "swoft/task": "^2.0", "swoft/redis": "^2.0", "swoft/proxy": "^2.0", + "swoft/error": "^2.0", "swoft/component": "dev-master as 2.0" }, "require-dev": { From f2ee008f86696b2a12b375e4867e36dec44245ec Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 14 Apr 2019 19:57:33 +0800 Subject: [PATCH 327/643] Added ab test --- app/Console/Command/TestCommand.php | 61 +++++++++++++++++++++++++++++ bin/test.php | 22 +---------- 2 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 app/Console/Command/TestCommand.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php new file mode 100644 index 00000000..07adf946 --- /dev/null +++ b/app/Console/Command/TestCommand.php @@ -0,0 +1,61 @@ +get('type', ''); + $uris = $this->uris(); + + // Format data + if (empty($type)) { + $exeUris = []; + foreach ($uris as $name => $uriAry) { + $exeUris = array_merge($exeUris, $uriAry); + } + } else { + $exeUris = $uris[$type] ?? []; + } + + foreach ($exeUris as $uri) { + $shell = \sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + + \output()->writeln('执行URL:' . $shell . PHP_EOL); + exec($shell, $result); + } + } + + /** + * @return array + */ + private function uris(): array + { + return [ + 'redis' => [ + '/redis/str', + '/redis/et', + '/redis/ep', + '/redis/release', + ], + 'log' => [ + '/log/test' + ] + ]; + } +} \ No newline at end of file diff --git a/bin/test.php b/bin/test.php index bef2d90e..c4c98af7 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,23 +1,3 @@ getMethods() as $method) { - $cms[] = strtolower($method->getName()); -} - -//var_dump($cms); - -$rc = new \ReflectionClass(\RedisCluster::class); - -$rcs = []; -foreach ($rc->getMethods() as $method) { - $rcs[] = strtolower($method->getName()); -} -//var_dump(count($rcs)); - -echo '['; -foreach (array_intersect($cms, $rcs) as $a){ - echo "'$a',".PHP_EOL; -} -echo ']'; \ No newline at end of file +system('ab -n 10000 -c 2000 127.0.0.1:18306/redis/et > /dev/null'); From dbd2608183f9a78c249f889e7ee9925dc37153c0 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 14 Apr 2019 19:58:04 +0800 Subject: [PATCH 328/643] Rm .keep --- app/Console/Command/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/Console/Command/.keep diff --git a/app/Console/Command/.keep b/app/Console/Command/.keep deleted file mode 100644 index e69de29b..00000000 From dae4d5bf4ef204b3940603c29e7c95c61b02233b Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 14 Apr 2019 22:58:07 +0800 Subject: [PATCH 329/643] add more example classes --- .env.example | 4 ++ README.md | 4 ++ app/Console/Command/DemoCommand.php | 18 ++++- .../Handler/HttpExceptionHandler.php | 31 +++++++- app/Http/Controller/HomeController.php | 33 +++++++++ app/WebSocket/Chat/HomeController.php | 48 +++++++++++++ app/WebSocket/ChatModule.php | 25 +++++-- app/WebSocket/EchoModule.php | 38 ++++++++++ app/bean.php | 13 +++- bak.composer.json | 70 +++++++++++++++++++ composer.json | 48 ++++--------- composer.dev.json => dev.composer.json | 0 12 files changed, 291 insertions(+), 41 deletions(-) create mode 100644 .env.example create mode 100644 README.md create mode 100644 app/Http/Controller/HomeController.php create mode 100644 app/WebSocket/Chat/HomeController.php create mode 100644 app/WebSocket/EchoModule.php create mode 100644 bak.composer.json rename composer.dev.json => dev.composer.json (100%) diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..1d0c4d73 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +APP_DEBUG = true +SWOFT_DEBUG = true +ENABLE_WS_SERVER = true + diff --git a/README.md b/README.md new file mode 100644 index 00000000..b2df6218 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Swoft + +⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole + diff --git a/app/Console/Command/DemoCommand.php b/app/Console/Command/DemoCommand.php index 4892d96e..a535d55a 100644 --- a/app/Console/Command/DemoCommand.php +++ b/app/Console/Command/DemoCommand.php @@ -2,11 +2,27 @@ namespace App\Console\Command; +use Swoft\Console\Annotation\Mapping\Command; +use Swoft\Console\Annotation\Mapping\CommandMapping; +use Swoft\Console\Helper\Show; +use Swoft\Console\Input\Input; + /** * Class DemoCommand * @package App\Console\Command + * @Command() */ class DemoCommand { - + /** + * @CommandMapping() + * @param Input $input + */ + public function test(Input $input): void + { + Show::prettyJSON([ + 'args' => $input->getArgs(), + 'opts' => $input->getOptions(), + ]); + } } diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 2d5cc003..3b543876 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -2,7 +2,36 @@ namespace App\Exception\Handler; -class HttpExceptionHandler +use Swoft\Error\Annotation\Mapping\ExceptionHandler; +use Swoft\Http\Message\Response; +use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; + +/** + * Class HttpExceptionHandler + * + * @ExceptionHandler(Exception::class) + */ +class HttpExceptionHandler extends AbstractHttpErrorHandler { + /** + * @param \Throwable $e + * @param Response $response + * @return Response + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + */ + public function handle(\Throwable $e, Response $response): Response + { + if (!\APP_DEBUG) { + return $response->withStatus(500)->withContent($e->getMessage()); + } + // Debug is true + return $response->withData([ + 'code' => $e->getCode(), + 'error' => $e->getMessage(), + 'file' => \sprintf('At %s line %d', $e->getFile(), $e->getLine()), + 'trace' => $e->getTraceAsString(), + ]); + } } diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php new file mode 100644 index 00000000..008c875a --- /dev/null +++ b/app/Http/Controller/HomeController.php @@ -0,0 +1,33 @@ +getResponse()->withContent('Hello'); + } + + /** + * @RequestMapping("/ex") + * @throws \Throwable + */ + public function ex(): void + { + throw new \RuntimeException('exception throw on ' . __METHOD__); + } +} diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php new file mode 100644 index 00000000..3ec7a46a --- /dev/null +++ b/app/WebSocket/Chat/HomeController.php @@ -0,0 +1,48 @@ +push('hi, this is home.index'); + } + + /** + * Message command is: 'home.echo' + * + * @param $data + * @MessageMapping() + */ + public function echo($data): void + { + Session::mustGet()->push('(home.echo)Recv: ' .$data); + } + + /** + * Message command is: 'home.ar' + * + * @param $data + * @MessageMapping("ar") + * @return string + */ + public function autoReply($data): string + { + return'(home.ar)Recv: ' .$data; + } +} diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index 3fe97828..620a8531 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -1,14 +1,31 @@ push($request->getFd(), "Opened, welcome!(FD: $fd)"); + } +} diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php new file mode 100644 index 00000000..bce32a51 --- /dev/null +++ b/app/WebSocket/EchoModule.php @@ -0,0 +1,38 @@ +push($request->getFd(), "Opened, welcome!(FD: $fd)"); + } + + /** + * @OnMessage() + * @param Server $server + * @param Frame $frame + */ + public function onMessage(Server $server, Frame $frame): void + { + $server->push($frame->fd, 'Recv: ' . $frame->data); + } +} diff --git a/app/bean.php b/app/bean.php index 2f67d22b..72dcbe44 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,4 +1,5 @@ [ 'flushRequest' => false, @@ -39,4 +40,14 @@ 'rpcServer' => [ 'class' => \Swoft\Rpc\Server\ServiceServer::class, ], -]; \ No newline at end of file + 'wsServer' => [ + 'on' => [ + // Enable http handle + \Swoft\Server\Swoole\SwooleEvent::REQUEST => bean(\Swoft\Http\Server\Swoole\RequestListener::class), + ], + /** @see \Swoft\WebSocket\Server\WebSocketServer::$setting */ + 'setting' => [ + 'log_file' => alias('@runtime/swoole.log'), + ], + ], +]; diff --git a/bak.composer.json b/bak.composer.json new file mode 100644 index 00000000..91ef62a5 --- /dev/null +++ b/bak.composer.json @@ -0,0 +1,70 @@ +{ + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", + "license": "Apache-2.0", + "require": { + "php": ">=7.1", + "ext-pdo": "*", + "ext-json": "*", + "ext-redis": "*", + "swoft/annotation": "^2.0", + "swoft/bean": "^2.0", + "swoft/event": "^2.0", + "swoft/aop": "^2.0", + "swoft/config": "^2.0", + "swoft/stdlib": "^2.0", + "swoft/framework": "^2.0", + "swoft/http-message": "^2.0", + "swoft/server": "^2.0", + "swoft/tcp-server": "^2.0", + "swoft/http-server": "^2.0", + "swoft/websocket-server": "^2.0", + "swoft/log": "^2.0", + "swoft/db": "^2.0", + "swoft/connection-pool": "^2.0", + "swoft/test": "^2.0", + "swoft/console": "^2.0", + "swoft/rpc": "^2.0", + "swoft/rpc-server": "^2.0", + "swoft/rpc-client": "^2.0", + "swoft/task": "^2.0", + "swoft/redis": "^2.0", + "swoft/proxy": "^2.0", + "swoft/component": "dev-master as 2.0" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^7.5", + "friendsofphp/php-cs-fixer": "^2.10" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Helper/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + } + }, + "scripts": { + }, + "repositories": [ + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-component.git" + }, + { + "type": "composer", + "url": "https://packagist.laravel-china.org/" + } + ] +} diff --git a/composer.json b/composer.json index 91ef62a5..20114619 100644 --- a/composer.json +++ b/composer.json @@ -13,35 +13,11 @@ "ext-pdo": "*", "ext-json": "*", "ext-redis": "*", - "swoft/annotation": "^2.0", - "swoft/bean": "^2.0", - "swoft/event": "^2.0", - "swoft/aop": "^2.0", - "swoft/config": "^2.0", - "swoft/stdlib": "^2.0", - "swoft/framework": "^2.0", - "swoft/http-message": "^2.0", - "swoft/server": "^2.0", - "swoft/tcp-server": "^2.0", - "swoft/http-server": "^2.0", - "swoft/websocket-server": "^2.0", - "swoft/log": "^2.0", - "swoft/db": "^2.0", - "swoft/connection-pool": "^2.0", - "swoft/test": "^2.0", - "swoft/console": "^2.0", - "swoft/rpc": "^2.0", - "swoft/rpc-server": "^2.0", - "swoft/rpc-client": "^2.0", - "swoft/task": "^2.0", - "swoft/redis": "^2.0", - "swoft/proxy": "^2.0", - "swoft/component": "dev-master as 2.0" + "swoft/view": "dev-master", + "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { - "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5", - "friendsofphp/php-cs-fixer": "^2.10" + "swoft/swoole-ide-helper": "dev-master" }, "autoload": { "psr-4": { @@ -57,14 +33,18 @@ }, "scripts": { }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-component.git" - }, - { + "repositories": { + "packagist": { "type": "composer", "url": "https://packagist.laravel-china.org/" + }, + "0": { + "type": "path", + "url": "../swoft-component" + }, + "1": { + "type": "path", + "url": "../swoft-view" } - ] + } } diff --git a/composer.dev.json b/dev.composer.json similarity index 100% rename from composer.dev.json rename to dev.composer.json From c57c602cb1ac91d8db261b14551e4b09cefb6dd5 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 14 Apr 2019 23:00:05 +0800 Subject: [PATCH 330/643] revert composer config --- bak.composer.json | 70 ----------------------------------------------- composer.json | 48 ++++++++++++++++++++++---------- 2 files changed, 34 insertions(+), 84 deletions(-) delete mode 100644 bak.composer.json diff --git a/bak.composer.json b/bak.composer.json deleted file mode 100644 index 91ef62a5..00000000 --- a/bak.composer.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", - "license": "Apache-2.0", - "require": { - "php": ">=7.1", - "ext-pdo": "*", - "ext-json": "*", - "ext-redis": "*", - "swoft/annotation": "^2.0", - "swoft/bean": "^2.0", - "swoft/event": "^2.0", - "swoft/aop": "^2.0", - "swoft/config": "^2.0", - "swoft/stdlib": "^2.0", - "swoft/framework": "^2.0", - "swoft/http-message": "^2.0", - "swoft/server": "^2.0", - "swoft/tcp-server": "^2.0", - "swoft/http-server": "^2.0", - "swoft/websocket-server": "^2.0", - "swoft/log": "^2.0", - "swoft/db": "^2.0", - "swoft/connection-pool": "^2.0", - "swoft/test": "^2.0", - "swoft/console": "^2.0", - "swoft/rpc": "^2.0", - "swoft/rpc-server": "^2.0", - "swoft/rpc-client": "^2.0", - "swoft/task": "^2.0", - "swoft/redis": "^2.0", - "swoft/proxy": "^2.0", - "swoft/component": "dev-master as 2.0" - }, - "require-dev": { - "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5", - "friendsofphp/php-cs-fixer": "^2.10" - }, - "autoload": { - "psr-4": { - "App\\": "app/" - }, - "files": [ - "app/Helper/Functions.php" - ] - }, - "autoload-dev": { - "psr-4": { - } - }, - "scripts": { - }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-component.git" - }, - { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - } - ] -} diff --git a/composer.json b/composer.json index 20114619..91ef62a5 100644 --- a/composer.json +++ b/composer.json @@ -13,11 +13,35 @@ "ext-pdo": "*", "ext-json": "*", "ext-redis": "*", - "swoft/view": "dev-master", - "swoft/component": "2.0.x-dev as 2.0" + "swoft/annotation": "^2.0", + "swoft/bean": "^2.0", + "swoft/event": "^2.0", + "swoft/aop": "^2.0", + "swoft/config": "^2.0", + "swoft/stdlib": "^2.0", + "swoft/framework": "^2.0", + "swoft/http-message": "^2.0", + "swoft/server": "^2.0", + "swoft/tcp-server": "^2.0", + "swoft/http-server": "^2.0", + "swoft/websocket-server": "^2.0", + "swoft/log": "^2.0", + "swoft/db": "^2.0", + "swoft/connection-pool": "^2.0", + "swoft/test": "^2.0", + "swoft/console": "^2.0", + "swoft/rpc": "^2.0", + "swoft/rpc-server": "^2.0", + "swoft/rpc-client": "^2.0", + "swoft/task": "^2.0", + "swoft/redis": "^2.0", + "swoft/proxy": "^2.0", + "swoft/component": "dev-master as 2.0" }, "require-dev": { - "swoft/swoole-ide-helper": "dev-master" + "swoft/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^7.5", + "friendsofphp/php-cs-fixer": "^2.10" }, "autoload": { "psr-4": { @@ -33,18 +57,14 @@ }, "scripts": { }, - "repositories": { - "packagist": { + "repositories": [ + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-component.git" + }, + { "type": "composer", "url": "https://packagist.laravel-china.org/" - }, - "0": { - "type": "path", - "url": "../swoft-component" - }, - "1": { - "type": "path", - "url": "../swoft-view" } - } + ] } From 2fcef213b3502efe50aeb434e600f88595cd31bf Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 14 Apr 2019 23:02:21 +0800 Subject: [PATCH 331/643] Dev 2.0 (#594) * add more example classes --- .env.example | 4 ++ README.md | 4 ++ app/Console/Command/DemoCommand.php | 28 +++++++++++ .../Handler/HttpExceptionHandler.php | 37 ++++++++++++++ app/Helper/Functions.php | 4 +- app/Http/Controller/HomeController.php | 33 +++++++++++++ app/WebSocket/Chat/HomeController.php | 48 +++++++++++++++++++ app/WebSocket/ChatModule.php | 25 ++++++++-- app/WebSocket/EchoModule.php | 38 +++++++++++++++ app/bean.php | 13 ++++- dev.composer.json | 45 +++++++++++++++++ runtime/.keep | 0 12 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 .env.example create mode 100644 README.md create mode 100644 app/Console/Command/DemoCommand.php create mode 100644 app/Exception/Handler/HttpExceptionHandler.php create mode 100644 app/Http/Controller/HomeController.php create mode 100644 app/WebSocket/Chat/HomeController.php create mode 100644 app/WebSocket/EchoModule.php create mode 100644 dev.composer.json create mode 100644 runtime/.keep diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..1d0c4d73 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +APP_DEBUG = true +SWOFT_DEBUG = true +ENABLE_WS_SERVER = true + diff --git a/README.md b/README.md new file mode 100644 index 00000000..b2df6218 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Swoft + +⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole + diff --git a/app/Console/Command/DemoCommand.php b/app/Console/Command/DemoCommand.php new file mode 100644 index 00000000..a535d55a --- /dev/null +++ b/app/Console/Command/DemoCommand.php @@ -0,0 +1,28 @@ + $input->getArgs(), + 'opts' => $input->getOptions(), + ]); + } +} diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php new file mode 100644 index 00000000..3b543876 --- /dev/null +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -0,0 +1,37 @@ +withStatus(500)->withContent($e->getMessage()); + } + + // Debug is true + return $response->withData([ + 'code' => $e->getCode(), + 'error' => $e->getMessage(), + 'file' => \sprintf('At %s line %d', $e->getFile(), $e->getLine()), + 'trace' => $e->getTraceAsString(), + ]); + } +} diff --git a/app/Helper/Functions.php b/app/Helper/Functions.php index a4abe2da..4261cdd9 100644 --- a/app/Helper/Functions.php +++ b/app/Helper/Functions.php @@ -1,2 +1,4 @@ getResponse()->withContent('Hello'); + } + + /** + * @RequestMapping("/ex") + * @throws \Throwable + */ + public function ex(): void + { + throw new \RuntimeException('exception throw on ' . __METHOD__); + } +} diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php new file mode 100644 index 00000000..3ec7a46a --- /dev/null +++ b/app/WebSocket/Chat/HomeController.php @@ -0,0 +1,48 @@ +push('hi, this is home.index'); + } + + /** + * Message command is: 'home.echo' + * + * @param $data + * @MessageMapping() + */ + public function echo($data): void + { + Session::mustGet()->push('(home.echo)Recv: ' .$data); + } + + /** + * Message command is: 'home.ar' + * + * @param $data + * @MessageMapping("ar") + * @return string + */ + public function autoReply($data): string + { + return'(home.ar)Recv: ' .$data; + } +} diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index 3fe97828..620a8531 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -1,14 +1,31 @@ push($request->getFd(), "Opened, welcome!(FD: $fd)"); + } +} diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php new file mode 100644 index 00000000..bce32a51 --- /dev/null +++ b/app/WebSocket/EchoModule.php @@ -0,0 +1,38 @@ +push($request->getFd(), "Opened, welcome!(FD: $fd)"); + } + + /** + * @OnMessage() + * @param Server $server + * @param Frame $frame + */ + public function onMessage(Server $server, Frame $frame): void + { + $server->push($frame->fd, 'Recv: ' . $frame->data); + } +} diff --git a/app/bean.php b/app/bean.php index 58f80bbc..1f7e6660 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,4 +1,5 @@ [ 'flushRequest' => false, @@ -39,4 +40,14 @@ 'rpcServer' => [ 'class' => \Swoft\Rpc\Server\ServiceServer::class, ], -]; \ No newline at end of file + 'wsServer' => [ + 'on' => [ + // Enable http handle + \Swoft\Server\Swoole\SwooleEvent::REQUEST => bean(\Swoft\Http\Server\Swoole\RequestListener::class), + ], + /** @see \Swoft\WebSocket\Server\WebSocketServer::$setting */ + 'setting' => [ + 'log_file' => alias('@runtime/swoole.log'), + ], + ], +]; diff --git a/dev.composer.json b/dev.composer.json new file mode 100644 index 00000000..effeb523 --- /dev/null +++ b/dev.composer.json @@ -0,0 +1,45 @@ +{ + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", + "license": "Apache-2.0", + "require": { + "php": ">=7.1", + "ext-pdo": "*", + "ext-json": "*", + "ext-redis": "*", + "swoft/component": "2.0.x-dev as 2.0" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Helper/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + } + }, + "scripts": { + }, + "repositories": [ + { + "type": "composer", + "url": "https://packagist.laravel-china.org/" + }, + { + "type": "path", + "url": "../swoft-component" + } + ] +} diff --git a/runtime/.keep b/runtime/.keep new file mode 100644 index 00000000..e69de29b From 97bb1ce19194209d0d3378815ab8cf83f697bdfe Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 15 Apr 2019 17:07:24 +0800 Subject: [PATCH 332/643] update some example --- .../Handler/HttpExceptionHandler.php | 5 ++-- app/Http/Controller/HomeController.php | 25 +++++++++++++++- resource/views/.keep | 0 resource/views/home/index.php | 13 +++++++++ resource/views/layouts/default.php | 25 ++++++++++++++++ resource/views/layouts/default/footer.php | 20 +++++++++++++ resource/views/layouts/default/header.php | 29 +++++++++++++++++++ 7 files changed, 114 insertions(+), 3 deletions(-) delete mode 100644 resource/views/.keep create mode 100644 resource/views/home/index.php create mode 100644 resource/views/layouts/default.php create mode 100644 resource/views/layouts/default/footer.php create mode 100644 resource/views/layouts/default/header.php diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 3b543876..cbdb60c3 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -9,7 +9,7 @@ /** * Class HttpExceptionHandler * - * @ExceptionHandler(Exception::class) + * @ExceptionHandler(\Throwable::class) */ class HttpExceptionHandler extends AbstractHttpErrorHandler { @@ -22,6 +22,7 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler */ public function handle(\Throwable $e, Response $response): Response { + // Debug is false if (!\APP_DEBUG) { return $response->withStatus(500)->withContent($e->getMessage()); } @@ -29,7 +30,7 @@ public function handle(\Throwable $e, Response $response): Response // Debug is true return $response->withData([ 'code' => $e->getCode(), - 'error' => $e->getMessage(), + 'error' => \sprintf('(%s) %s', \get_class($e), $e->getMessage()), 'file' => \sprintf('At %s line %d', $e->getFile(), $e->getLine()), 'trace' => $e->getTraceAsString(), ]); diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 008c875a..c629279b 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -3,9 +3,11 @@ namespace App\Http\Controller; use Swoft\Context\Context; +use Swoft\Http\Message\ContentType; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\View\Renderer; /** * Class HomeController @@ -19,7 +21,28 @@ class HomeController */ public function index(): Response { - return Context::mustGet()->getResponse()->withContent('Hello'); + /** @var Renderer $renderer */ + $renderer = \Swoft::getBean('view'); + $content = $renderer->render('home/index'); + + return Context::mustGet() + ->getResponse() + ->withContentType(ContentType::HTML) + ->withContent($content); + } + + /** + * @RequestMapping("/hello[/{name}]") + * @param string $name + * @return Response + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + */ + public function hello(string $name): Response + { + return Context::mustGet() + ->getResponse() + ->withContent('Hello' . ($name === '' ? '' : ", {$name}")); } /** diff --git a/resource/views/.keep b/resource/views/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/resource/views/home/index.php b/resource/views/home/index.php new file mode 100644 index 00000000..878b88d2 --- /dev/null +++ b/resource/views/home/index.php @@ -0,0 +1,13 @@ + + + + + + + Swoft Framework + + +

      Hello, world!

      + + diff --git a/resource/views/layouts/default.php b/resource/views/layouts/default.php new file mode 100644 index 00000000..9d8b16ff --- /dev/null +++ b/resource/views/layouts/default.php @@ -0,0 +1,25 @@ + + + + + + + + Demo for layout + + + +include('layouts/default/header') ?> + +
      + +
      {_CONTENT_}
      + include('layouts/default/footer') ?> +
      + + diff --git a/resource/views/layouts/default/footer.php b/resource/views/layouts/default/footer.php new file mode 100644 index 00000000..3dff0851 --- /dev/null +++ b/resource/views/layouts/default/footer.php @@ -0,0 +1,20 @@ + diff --git a/resource/views/layouts/default/header.php b/resource/views/layouts/default/header.php new file mode 100644 index 00000000..ea14e7b9 --- /dev/null +++ b/resource/views/layouts/default/header.php @@ -0,0 +1,29 @@ +
      + +
      From 3c5261ffa8ecea15dcae085db259af063f22e4cd Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 15 Apr 2019 19:23:41 +0800 Subject: [PATCH 333/643] Refactor db --- app/Http/Controller/DbBuilderController.php | 10 ++++++++++ app/Http/Controller/DbModelController.php | 10 ++++++++++ app/Http/Controller/DbTransactionController.php | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 app/Http/Controller/DbBuilderController.php create mode 100644 app/Http/Controller/DbModelController.php create mode 100644 app/Http/Controller/DbTransactionController.php diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php new file mode 100644 index 00000000..e86a1f8f --- /dev/null +++ b/app/Http/Controller/DbBuilderController.php @@ -0,0 +1,10 @@ + Date: Mon, 15 Apr 2019 19:26:35 +0800 Subject: [PATCH 334/643] Refactor db (#596) --- app/Http/Controller/DbBuilderController.php | 10 ++++++++++ app/Http/Controller/DbModelController.php | 10 ++++++++++ app/Http/Controller/DbTransactionController.php | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 app/Http/Controller/DbBuilderController.php create mode 100644 app/Http/Controller/DbModelController.php create mode 100644 app/Http/Controller/DbTransactionController.php diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php new file mode 100644 index 00000000..e86a1f8f --- /dev/null +++ b/app/Http/Controller/DbBuilderController.php @@ -0,0 +1,10 @@ + Date: Tue, 16 Apr 2019 10:05:41 +0800 Subject: [PATCH 335/643] Add db demo --- app/Console/Command/TestCommand.php | 18 ++- app/Http/Controller/DbBuilderController.php | 6 + .../Controller/DbTransactionController.php | 136 +++++++++++++++++- app/bean.php | 2 +- 4 files changed, 157 insertions(+), 5 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 07adf946..89a96b5f 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -34,10 +34,14 @@ public function ab() } foreach ($exeUris as $uri) { - $shell = \sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $abShell = \sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $curlShell = \sprintf('curl 127.0.0.1:18306%s', $uri); - \output()->writeln('执行URL:' . $shell . PHP_EOL); - exec($shell, $result); + exec($curlShell, $curlResult); + \output()->writeln('执行结果:' . json_encode($curlResult)); + \output()->writeln('执行URL:' . $abShell . PHP_EOL); + + exec($abShell, $abResult); } } @@ -55,6 +59,14 @@ private function uris(): array ], 'log' => [ '/log/test' + ], + 'db' => [ + '/dbTransaction/ts', + '/dbTransaction/cm', + '/dbTransaction/rl', + '/dbTransaction/ts2', + '/dbTransaction/cm2', + '/dbTransaction/rl2', ] ]; } diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php index e86a1f8f..13cece37 100644 --- a/app/Http/Controller/DbBuilderController.php +++ b/app/Http/Controller/DbBuilderController.php @@ -3,7 +3,13 @@ namespace App\Http\Controller; +use Swoft\Http\Server\Annotation\Mapping\Controller; +/** + * Class DbBuilderController + * + * @since 2.0 + */ class DbBuilderController { diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 54f630ed..29e43b6a 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -3,8 +3,142 @@ namespace App\Http\Controller; +use App\Model\Entity\User; +use Swoft\Db\DB; +use Swoft\Http\Server\Annotation\Mapping\Controller; +use Swoft\Http\Server\Annotation\Mapping\RequestMapping; -class TransactionController +/** + * Class DbTransactionController + * + * @since 2.0 + * + * @Controller("dbTransaction") + */ +class DbTransactionController { + /** + * @RequestMapping(route="ts") + * + * @return false|string + */ + public function ts() + { + DB::beginTransaction(); + $user = User::find(22); + \sgo(function () { + DB::beginTransaction(); + User::find(22); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="cm") + * + * @return false|string + */ + public function cm() + { + DB::beginTransaction(); + $user = User::find(22); + DB::commit(); + + \sgo(function () { + DB::beginTransaction(); + User::find(22); + DB::commit(); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="rl") + * + * @return false|string + */ + public function rl() + { + DB::beginTransaction(); + $user = User::find(22); + DB::rollBack(); + + \sgo(function () { + DB::beginTransaction(); + User::find(22); + DB::rollBack(); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="ts2") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function ts2() + { + DB::connection()->beginTransaction(); + $user = User::find(22); + + \sgo(function () { + DB::connection()->beginTransaction(); + User::find(22); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="cm2") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function cm2() + { + DB::connection()->beginTransaction(); + $user = User::find(22); + DB::connection()->commit(); + + \sgo(function () { + DB::connection()->beginTransaction(); + User::find(22); + DB::connection()->commit(); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="rl2") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function rl2() + { + DB::connection()->beginTransaction(); + $user = User::find(22); + DB::connection()->rollBack(); + + \sgo(function () { + DB::connection()->beginTransaction(); + User::find(22); + DB::connection()->rollBack(); + }); + + return json_encode($user->toArray()); + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 1f7e6660..50e1b7c0 100644 --- a/app/bean.php +++ b/app/bean.php @@ -35,7 +35,7 @@ ], 'user.pool' => [ 'class' => \Swoft\Rpc\Client\Pool::class, - 'client' => bean('user') + 'client' => \bean('user') ], 'rpcServer' => [ 'class' => \Swoft\Rpc\Server\ServiceServer::class, From bd6ee25c76f3a5e153f18fa5701b973c2015639b Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 16 Apr 2019 10:23:14 +0800 Subject: [PATCH 336/643] add demo (#597) * Refactor db * Add db demo --- app/Console/Command/TestCommand.php | 18 ++- app/Http/Controller/DbBuilderController.php | 6 +- .../Controller/DbTransactionController.php | 136 +++++++++++++++++- app/bean.php | 2 +- 4 files changed, 156 insertions(+), 6 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 07adf946..89a96b5f 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -34,10 +34,14 @@ public function ab() } foreach ($exeUris as $uri) { - $shell = \sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $abShell = \sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $curlShell = \sprintf('curl 127.0.0.1:18306%s', $uri); - \output()->writeln('执行URL:' . $shell . PHP_EOL); - exec($shell, $result); + exec($curlShell, $curlResult); + \output()->writeln('执行结果:' . json_encode($curlResult)); + \output()->writeln('执行URL:' . $abShell . PHP_EOL); + + exec($abShell, $abResult); } } @@ -55,6 +59,14 @@ private function uris(): array ], 'log' => [ '/log/test' + ], + 'db' => [ + '/dbTransaction/ts', + '/dbTransaction/cm', + '/dbTransaction/rl', + '/dbTransaction/ts2', + '/dbTransaction/cm2', + '/dbTransaction/rl2', ] ]; } diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php index e86a1f8f..93b5539d 100644 --- a/app/Http/Controller/DbBuilderController.php +++ b/app/Http/Controller/DbBuilderController.php @@ -3,7 +3,11 @@ namespace App\Http\Controller; - +/** + * Class DbBuilderController + * + * @since 2.0 + */ class DbBuilderController { diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 54f630ed..29e43b6a 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -3,8 +3,142 @@ namespace App\Http\Controller; +use App\Model\Entity\User; +use Swoft\Db\DB; +use Swoft\Http\Server\Annotation\Mapping\Controller; +use Swoft\Http\Server\Annotation\Mapping\RequestMapping; -class TransactionController +/** + * Class DbTransactionController + * + * @since 2.0 + * + * @Controller("dbTransaction") + */ +class DbTransactionController { + /** + * @RequestMapping(route="ts") + * + * @return false|string + */ + public function ts() + { + DB::beginTransaction(); + $user = User::find(22); + \sgo(function () { + DB::beginTransaction(); + User::find(22); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="cm") + * + * @return false|string + */ + public function cm() + { + DB::beginTransaction(); + $user = User::find(22); + DB::commit(); + + \sgo(function () { + DB::beginTransaction(); + User::find(22); + DB::commit(); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="rl") + * + * @return false|string + */ + public function rl() + { + DB::beginTransaction(); + $user = User::find(22); + DB::rollBack(); + + \sgo(function () { + DB::beginTransaction(); + User::find(22); + DB::rollBack(); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="ts2") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function ts2() + { + DB::connection()->beginTransaction(); + $user = User::find(22); + + \sgo(function () { + DB::connection()->beginTransaction(); + User::find(22); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="cm2") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function cm2() + { + DB::connection()->beginTransaction(); + $user = User::find(22); + DB::connection()->commit(); + + \sgo(function () { + DB::connection()->beginTransaction(); + User::find(22); + DB::connection()->commit(); + }); + + return json_encode($user->toArray()); + } + + /** + * @RequestMapping(route="rl2") + * + * @return false|string + * @throws \ReflectionException + * @throws \Swoft\Bean\Exception\ContainerException + * @throws \Swoft\Db\Exception\PoolException + */ + public function rl2() + { + DB::connection()->beginTransaction(); + $user = User::find(22); + DB::connection()->rollBack(); + + \sgo(function () { + DB::connection()->beginTransaction(); + User::find(22); + DB::connection()->rollBack(); + }); + + return json_encode($user->toArray()); + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 1f7e6660..50e1b7c0 100644 --- a/app/bean.php +++ b/app/bean.php @@ -35,7 +35,7 @@ ], 'user.pool' => [ 'class' => \Swoft\Rpc\Client\Pool::class, - 'client' => bean('user') + 'client' => \bean('user') ], 'rpcServer' => [ 'class' => \Swoft\Rpc\Server\ServiceServer::class, From c164e0dd97c1c5ffae5b783e339c674767e6bf0b Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 17 Apr 2019 20:06:30 +0800 Subject: [PATCH 337/643] Fix ab (#598) * Refactor db * Add db demo From 2c0a85857d0fa172b2f13b32ad178a0e4fffea8d Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 18 Apr 2019 18:22:16 +0800 Subject: [PATCH 338/643] Add dem (#600) * Refactor db * Add db demo From 72c1658fbdc0f7486ed7d79486a4372e9e9b0f4c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 21 Apr 2019 20:55:28 +0800 Subject: [PATCH 339/643] Modify error --- app/Exception/Handler/HttpExceptionHandler.php | 13 +++++++++---- bin/test.php | 6 +++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 3b543876..6b789c51 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -16,6 +16,7 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler /** * @param \Throwable $e * @param Response $response + * * @return Response * @throws \ReflectionException * @throws \Swoft\Bean\Exception\ContainerException @@ -23,15 +24,19 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler public function handle(\Throwable $e, Response $response): Response { if (!\APP_DEBUG) { - return $response->withStatus(500)->withContent($e->getMessage()); + return $response->withStatus(500)->withContent( + \sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()) + ); } - // Debug is true - return $response->withData([ + $data = [ 'code' => $e->getCode(), 'error' => $e->getMessage(), 'file' => \sprintf('At %s line %d', $e->getFile(), $e->getLine()), 'trace' => $e->getTraceAsString(), - ]); + ]; + + // Debug is true + return $response->withData($data); } } diff --git a/bin/test.php b/bin/test.php index c4c98af7..a113efe3 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,3 +1,7 @@ /dev/null'); +$fruits = array("apple","banana","pear"); +$numbered = array("1","2","3","pear"); +$cards = array_merge($fruits, $numbered); +print_r($cards); +print_r(array_unique($cards)); \ No newline at end of file From 7fc4b27306c61b7246bba59f9319d92469531483 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 26 Apr 2019 10:31:47 +0800 Subject: [PATCH 340/643] Add view demo --- .../Controller/DbTransactionController.php | 25 ++++++------- app/Http/Controller/ViewController.php | 35 +++++++++++++++++++ app/bean.php | 3 +- 3 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 app/Http/Controller/ViewController.php diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 29e43b6a..fe6fc6b1 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -4,6 +4,7 @@ namespace App\Http\Controller; use App\Model\Entity\User; +use mysql_xdevapi\Exception; use Swoft\Db\DB; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; @@ -25,11 +26,11 @@ class DbTransactionController public function ts() { DB::beginTransaction(); - $user = User::find(22); + $user = User::find(296); \sgo(function () { DB::beginTransaction(); - User::find(22); + User::find(296); }); return json_encode($user->toArray()); @@ -43,12 +44,12 @@ public function ts() public function cm() { DB::beginTransaction(); - $user = User::find(22); + $user = User::find(296); DB::commit(); \sgo(function () { DB::beginTransaction(); - User::find(22); + User::find(296); DB::commit(); }); @@ -63,12 +64,12 @@ public function cm() public function rl() { DB::beginTransaction(); - $user = User::find(22); + $user = User::find(296); DB::rollBack(); \sgo(function () { DB::beginTransaction(); - User::find(22); + User::find(296); DB::rollBack(); }); @@ -86,11 +87,11 @@ public function rl() public function ts2() { DB::connection()->beginTransaction(); - $user = User::find(22); + $user = User::find(296); \sgo(function () { DB::connection()->beginTransaction(); - User::find(22); + User::find(296); }); return json_encode($user->toArray()); @@ -107,12 +108,12 @@ public function ts2() public function cm2() { DB::connection()->beginTransaction(); - $user = User::find(22); + $user = User::find(296); DB::connection()->commit(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(22); + User::find(296); DB::connection()->commit(); }); @@ -130,12 +131,12 @@ public function cm2() public function rl2() { DB::connection()->beginTransaction(); - $user = User::find(22); + $user = User::find(296); DB::connection()->rollBack(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(22); + User::find(296); DB::connection()->rollBack(); }); diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php new file mode 100644 index 00000000..b98eccfa --- /dev/null +++ b/app/Http/Controller/ViewController.php @@ -0,0 +1,35 @@ +withContent('

      Swoft framework

      '); + $response = $response->withContentType(ContentType::HTML); + return $response; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 50e1b7c0..0c409267 100644 --- a/app/bean.php +++ b/app/bean.php @@ -8,7 +8,8 @@ ], 'httpServer' => [ 'class' => \Swoft\Http\Server\HttpServer::class, - 'port' => 18306, +// 'port' => 18306, + 'port' => 88, 'listener' => [ 'rpc' => \bean('rpcServer') ], From 136781b519cf7f08f89232ad6e10e9b5a942b1cf Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 26 Apr 2019 17:03:15 +0800 Subject: [PATCH 341/643] Add task demo --- app/Console/Command/TestCommand.php | 6 +++ app/Http/Controller/TaskController.php | 73 ++++++++++++++++++++++++++ app/Task/Listener/FinishListener.php | 18 ++++++- app/Task/Task/AsyncTask.php | 10 ---- app/Task/Task/CoTask.php | 10 ---- app/Task/Task/TestTask.php | 50 ++++++++++++++++++ app/bean.php | 52 +++++++++++++----- bin/swoft | 4 ++ 8 files changed, 187 insertions(+), 36 deletions(-) create mode 100644 app/Http/Controller/TaskController.php delete mode 100644 app/Task/Task/AsyncTask.php delete mode 100644 app/Task/Task/CoTask.php create mode 100644 app/Task/Task/TestTask.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 89a96b5f..531c7763 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -67,6 +67,12 @@ private function uris(): array '/dbTransaction/ts2', '/dbTransaction/cm2', '/dbTransaction/rl2', + ], + 'task' => [ + '/task/getListByCo', + '/task/deleteByCo', + '/task/getListByAsync', + '/task/deleteByAsync', ] ]; } diff --git a/app/Http/Controller/TaskController.php b/app/Http/Controller/TaskController.php new file mode 100644 index 00000000..d6866bc1 --- /dev/null +++ b/app/Http/Controller/TaskController.php @@ -0,0 +1,73 @@ +getTaskUniqid()); + } } \ No newline at end of file diff --git a/app/Task/Task/AsyncTask.php b/app/Task/Task/AsyncTask.php deleted file mode 100644 index 9f138ebc..00000000 --- a/app/Task/Task/AsyncTask.php +++ /dev/null @@ -1,10 +0,0 @@ - [1, 3, 3], + 'id' => $id, + 'default' => $default + ]; + } + + /** + * @TaskMapping() + * + * @param int $id + * + * @return bool + */ + public function delete(int $id): bool + { + if ($id > 10) { + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 0c409267..1ab61e65 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,29 +1,53 @@ [ - 'flushRequest' => false, - 'enable' => false, + 'flushRequest' => true, + 'enable' => true, 'json' => false, ], 'httpServer' => [ - 'class' => \Swoft\Http\Server\HttpServer::class, -// 'port' => 18306, - 'port' => 88, + 'class' => HttpServer::class, + 'port' => 18306, 'listener' => [ 'rpc' => \bean('rpcServer') ], 'on' => [ - \Swoft\Server\Swoole\SwooleEvent::TASK => \bean(\Swoft\Task\Swoole\TaskListener::class), - \Swoft\Server\Swoole\SwooleEvent::FINISH => \bean(\Swoft\Task\Swoole\FinishListener::class) + SwooleEvent::TASK => \bean(TaskListener::class), // Enable task must task and finish event + SwooleEvent::FINISH => \bean(FinishListener::class) ], + /* @see HttpServer::$setting */ 'setting' => [ - 'task_worker_num' => 1, + 'task_worker_num' => 4, 'task_enable_coroutine' => true ] ], + 'db' => [ + 'class' => Database::class, + 'dsn' => 'mysql:dbname=test;host=172.17.0.1', + 'username' => 'root', + 'password' => 'swoft123456', + ], + 'redis' => [ + 'class' => RedisDb::class, + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0, + ], 'user' => [ - 'class' => \Swoft\Rpc\Client\Client::class, + 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, 'setting' => [ @@ -35,18 +59,18 @@ 'packet' => \bean('rpcClientPacket') ], 'user.pool' => [ - 'class' => \Swoft\Rpc\Client\Pool::class, + 'class' => ServicePool::class, 'client' => \bean('user') ], 'rpcServer' => [ - 'class' => \Swoft\Rpc\Server\ServiceServer::class, + 'class' => ServiceServer::class, ], 'wsServer' => [ + 'class' => WebSocketServer::class, 'on' => [ - // Enable http handle - \Swoft\Server\Swoole\SwooleEvent::REQUEST => bean(\Swoft\Http\Server\Swoole\RequestListener::class), + SwooleEvent::REQUEST => \bean(RequestListener::class), // Enable http handle ], - /** @see \Swoft\WebSocket\Server\WebSocketServer::$setting */ + /* @see WebSocketServer::$setting */ 'setting' => [ 'log_file' => alias('@runtime/swoole.log'), ], diff --git a/bin/swoft b/bin/swoft index 970afd03..8b1a3eb2 100644 --- a/bin/swoft +++ b/bin/swoft @@ -6,5 +6,9 @@ require_once __DIR__ . '/bootstrap.php'; \Swoole\Runtime::enableCoroutine(); +Swoole\Coroutine::set([ + 'max_coroutine' => 300000, +]); + // Run application (new \App\Application())->run(); \ No newline at end of file From 837ba9e056011bba30de8d739127c080b5676548 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 26 Apr 2019 20:42:30 +0800 Subject: [PATCH 342/643] update some info --- .env.example | 6 ++---- app/Http/Controller/HomeController.php | 9 +++++++++ app/bean.php | 1 + composer.json | 2 +- config/base.php | 5 +++-- config/user/info.php | 5 +++++ dev.composer.json | 21 ++++++++++++++++----- 7 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 config/user/info.php diff --git a/.env.example b/.env.example index 1d0c4d73..7f3f9910 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,2 @@ -APP_DEBUG = true -SWOFT_DEBUG = true -ENABLE_WS_SERVER = true - +APP_DEBUG = 0 +SWOFT_DEBUG = 0 diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index c629279b..0b190b5a 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -53,4 +53,13 @@ public function ex(): void { throw new \RuntimeException('exception throw on ' . __METHOD__); } + + /** + * @RequestMapping("/er") + * @throws \Throwable + */ + public function er(): void + { + \trigger_error('user error', \E_USER_ERROR); + } } diff --git a/app/bean.php b/app/bean.php index 1f7e6660..df273997 100644 --- a/app/bean.php +++ b/app/bean.php @@ -45,6 +45,7 @@ // Enable http handle \Swoft\Server\Swoole\SwooleEvent::REQUEST => bean(\Swoft\Http\Server\Swoole\RequestListener::class), ], + 'debug' => env('SWOFT_DEBUG', 1), /** @see \Swoft\WebSocket\Server\WebSocketServer::$setting */ 'setting' => [ 'log_file' => alias('@runtime/swoole.log'), diff --git a/composer.json b/composer.json index 2a0d91a8..016b035a 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", "license": "Apache-2.0", "require": { - "php": ">=7.1", + "php": ">7.1", "ext-pdo": "*", "ext-json": "*", "swoft/annotation": "^2.0", diff --git a/config/base.php b/config/base.php index 7b8e33db..30c55b9a 100644 --- a/config/base.php +++ b/config/base.php @@ -1,4 +1,5 @@ 'swoft framework 2.0' -]; \ No newline at end of file + 'name' => 'Swoft framework 2.0', + 'debug' => env('SWOFT_DEBUG', 1), +]; diff --git a/config/user/info.php b/config/user/info.php new file mode 100644 index 00000000..ca5d8ed5 --- /dev/null +++ b/config/user/info.php @@ -0,0 +1,5 @@ +=7.1", + "php": ">7.1", "ext-pdo": "*", "ext-json": "*", "ext-redis": "*", + "swoft/view": "dev-master", + "swoft/devtool": "dev-master", "swoft/component": "2.0.x-dev as 2.0" }, "require-dev": { + "phpunit/phpunit": "^7.5", "swoft/swoole-ide-helper": "dev-master" }, "autoload": { @@ -32,14 +35,22 @@ }, "scripts": { }, - "repositories": [ - { + "repositories": { + "packagist": { "type": "composer", "url": "https://packagist.laravel-china.org/" }, - { + "0": { "type": "path", "url": "../swoft-component" + }, + "1": { + "type": "path", + "url": "../swoft-view" + }, + "2": { + "type": "path", + "url": "../swoft-devtool" } - ] + } } From 61fe03b1185c1c4bb3a9e1648e8eccd8254b7ec4 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 28 Apr 2019 10:28:00 +0800 Subject: [PATCH 343/643] add rpc demo --- app/Console/Command/TestCommand.php | 5 ++ .../Controller/DbTransactionController.php | 24 +++--- app/Http/Controller/RpcController.php | 75 +++++++++++++++++++ app/Rpc/Lib/UserInterface.php | 19 +++++ app/Rpc/Middleware/ServiceMiddleware.php | 9 +++ app/Rpc/Service/UserService.php | 33 ++++++++ app/Rpc/Service/UserServiceV2.php | 36 +++++++++ app/Rpc/Service/big.data | 1 + app/bean.php | 2 +- bin/test.php | 6 -- 10 files changed, 191 insertions(+), 19 deletions(-) create mode 100644 app/Http/Controller/RpcController.php create mode 100644 app/Rpc/Service/big.data diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 531c7763..a2c22daa 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -73,6 +73,11 @@ private function uris(): array '/task/deleteByCo', '/task/getListByAsync', '/task/deleteByAsync', + ], + 'rpc' => [ + '/rpc/getList', + '/rpc/returnBool', + '/rpc/bigString', ] ]; } diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index fe6fc6b1..ca2fe489 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -26,11 +26,11 @@ class DbTransactionController public function ts() { DB::beginTransaction(); - $user = User::find(296); + $user = User::find(37978); \sgo(function () { DB::beginTransaction(); - User::find(296); + User::find(37978); }); return json_encode($user->toArray()); @@ -44,12 +44,12 @@ public function ts() public function cm() { DB::beginTransaction(); - $user = User::find(296); + $user = User::find(37978); DB::commit(); \sgo(function () { DB::beginTransaction(); - User::find(296); + User::find(37978); DB::commit(); }); @@ -64,12 +64,12 @@ public function cm() public function rl() { DB::beginTransaction(); - $user = User::find(296); + $user = User::find(37978); DB::rollBack(); \sgo(function () { DB::beginTransaction(); - User::find(296); + User::find(37978); DB::rollBack(); }); @@ -87,11 +87,11 @@ public function rl() public function ts2() { DB::connection()->beginTransaction(); - $user = User::find(296); + $user = User::find(37978); \sgo(function () { DB::connection()->beginTransaction(); - User::find(296); + User::find(37978); }); return json_encode($user->toArray()); @@ -108,12 +108,12 @@ public function ts2() public function cm2() { DB::connection()->beginTransaction(); - $user = User::find(296); + $user = User::find(37978); DB::connection()->commit(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(296); + User::find(37978); DB::connection()->commit(); }); @@ -131,12 +131,12 @@ public function cm2() public function rl2() { DB::connection()->beginTransaction(); - $user = User::find(296); + $user = User::find(37978); DB::connection()->rollBack(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(296); + User::find(37978); DB::connection()->rollBack(); }); diff --git a/app/Http/Controller/RpcController.php b/app/Http/Controller/RpcController.php new file mode 100644 index 00000000..277f1e4c --- /dev/null +++ b/app/Http/Controller/RpcController.php @@ -0,0 +1,75 @@ +userService->getList(12, 'type'); + $result2 = $this->userService2->getList(12, 'type'); + + return [$result, $result2]; + } + + /** + * @RequestMapping("returnBool") + * + * @return array + */ + public function returnBool(): array + { + $result = $this->userService->delete(12); + + if (is_bool($result)) { + return ['bool']; + } + + return ['notBool']; + } + + /** + * @RequestMapping() + * + * @return array + */ + public function bigString(): array + { + $string = $this->userService->getBigContent(); + + return ['string']; + } +} \ No newline at end of file diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index 667d55b9..9d646d5b 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -10,5 +10,24 @@ */ interface UserInterface { + /** + * @param int $id + * @param mixed $type + * @param int $count + * + * @return array + */ + public function getList(int $id, $type, int $count = 10): array; + /** + * @param int $id + * + * @return bool + */ + public function delete(int $id): bool; + + /** + * @return string + */ + public function getBigContent(): string; } \ No newline at end of file diff --git a/app/Rpc/Middleware/ServiceMiddleware.php b/app/Rpc/Middleware/ServiceMiddleware.php index 33d157e9..7d594705 100644 --- a/app/Rpc/Middleware/ServiceMiddleware.php +++ b/app/Rpc/Middleware/ServiceMiddleware.php @@ -4,6 +4,7 @@ namespace App\Rpc\Middleware; +use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Rpc\Server\Contract\MiddlewareInterface; use Swoft\Rpc\Server\Contract\RequestHandlerInterface; use Swoft\Rpc\Server\Contract\RequestInterface; @@ -13,9 +14,17 @@ * Class ServiceMiddleware * * @since 2.0 + * + * @Bean() */ class ServiceMiddleware implements MiddlewareInterface { + /** + * @param RequestInterface $request + * @param RequestHandlerInterface $requestHandler + * + * @return ResponseInterface + */ public function process(RequestInterface $request, RequestHandlerInterface $requestHandler): ResponseInterface { return $requestHandler->handle($request); diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index 4010de1d..09196540 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -5,13 +5,46 @@ use App\Rpc\Lib\UserInterface; +use Swoft\Co; +use Swoft\Rpc\Server\Annotation\Mapping\Service; /** * Class UserService * * @since 2.0 + * + * @Service() */ class UserService implements UserInterface { + /** + * @param int $id + * @param mixed $type + * @param int $count + * + * @return array + */ + public function getList(int $id, $type, int $count = 10): array + { + return ['name' => ['list']]; + } + + /** + * @param int $id + * + * @return bool + */ + public function delete(int $id): bool + { + return false; + } + /** + * @return string + */ + public function getBigContent(): string + { + $content = Co::readFile(__DIR__ . '/big.data'); + return $content; + } } \ No newline at end of file diff --git a/app/Rpc/Service/UserServiceV2.php b/app/Rpc/Service/UserServiceV2.php index f17b07f8..e42ac96c 100644 --- a/app/Rpc/Service/UserServiceV2.php +++ b/app/Rpc/Service/UserServiceV2.php @@ -5,13 +5,49 @@ use App\Rpc\Lib\UserInterface; +use Swoft\Co; +use Swoft\Rpc\Server\Annotation\Mapping\Service; /** * Class UserServiceV2 * * @since 2.0 + * + * @Service(version="1.2") */ class UserServiceV2 implements UserInterface { + /** + * @param int $id + * @param mixed $type + * @param int $count + * + * @return array + */ + public function getList(int $id, $type, int $count = 10): array + { + return [ + 'name' => ['list'], + 'v' => '1.2' + ]; + } + + /** + * @param int $id + * + * @return bool + */ + public function delete(int $id): bool + { + return false; + } + /** + * @return string + */ + public function getBigContent(): string + { + $content = Co::readFile(__DIR__ . '/big.data'); + return $content; + } } \ No newline at end of file diff --git a/app/Rpc/Service/big.data b/app/Rpc/Service/big.data new file mode 100644 index 00000000..2d7a101f --- /dev/null +++ b/app/Rpc/Service/big.data @@ -0,0 +1 @@ +2019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) 5cc415d7276b9-400012019/04/27-08:44:15 [INFO] App\Task\Listener\FinishListener:handle(53) packet-over \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 1ab61e65..5847d226 100644 --- a/app/bean.php +++ b/app/bean.php @@ -15,7 +15,7 @@ return [ 'logger' => [ 'flushRequest' => true, - 'enable' => true, + 'enable' => false, 'json' => false, ], 'httpServer' => [ diff --git a/bin/test.php b/bin/test.php index a113efe3..b3d9bbc7 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,7 +1 @@ Date: Sun, 28 Apr 2019 11:35:46 +0800 Subject: [PATCH 344/643] fix --- bin/test.php | 7 ------- dev.composer.json | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 bin/test.php diff --git a/bin/test.php b/bin/test.php deleted file mode 100644 index a113efe3..00000000 --- a/bin/test.php +++ /dev/null @@ -1,7 +0,0 @@ - Date: Sun, 28 Apr 2019 11:40:49 +0800 Subject: [PATCH 345/643] up --- dev.composer.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dev.composer.json b/dev.composer.json index 0af82a17..847c6da2 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -44,10 +44,6 @@ "type": "path", "url": "../swoft-component" }, - "1": { - "type": "path", - "url": "../swoft-view" - }, "2": { "type": "path", "url": "../swoft-devtool" From 6a166dd36e086b81b000f07291f02c32290988ed Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 28 Apr 2019 16:38:08 +0800 Subject: [PATCH 346/643] add multi demo --- app/Console/Command/TestCommand.php | 3 + app/Http/Controller/CoController.php | 74 +++++++++++++++++++ .../Controller/DbTransactionController.php | 24 +++--- app/bean.php | 2 +- 4 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 app/Http/Controller/CoController.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index a2c22daa..7dca9f3f 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -78,6 +78,9 @@ private function uris(): array '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', + ], + 'co' => [ + '/co/multi' ] ]; } diff --git a/app/Http/Controller/CoController.php b/app/Http/Controller/CoController.php new file mode 100644 index 00000000..abdb854b --- /dev/null +++ b/app/Http/Controller/CoController.php @@ -0,0 +1,74 @@ + [$this, 'addUser'], + 'getUser' => "App\Http\Controller\CoController::getUser", + 'curl' => function () { + $cli = new Client('127.0.0.1', 18306); + $cli->get('/redis/str'); + $result = $cli->body; + $cli->close(); + + return $result; + } + ]; + + $response = Co::multi($requests); + + return $response; + } + + /** + * @return array + */ + public static function getUser(): array + { + $result = Redis::set('key', 'value'); + + return [$result, Redis::get('key')]; + } + + /** + * @return array + * @throws \Throwable + */ + public function addUser(): array + { + $user = User::new(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + // Save result + $result = $user->save(); + + return [$result, $user->getId()]; + } +} \ No newline at end of file diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index ca2fe489..8fdbab2e 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -26,11 +26,11 @@ class DbTransactionController public function ts() { DB::beginTransaction(); - $user = User::find(37978); + $user = User::find(1); \sgo(function () { DB::beginTransaction(); - User::find(37978); + User::find(1); }); return json_encode($user->toArray()); @@ -44,12 +44,12 @@ public function ts() public function cm() { DB::beginTransaction(); - $user = User::find(37978); + $user = User::find(1); DB::commit(); \sgo(function () { DB::beginTransaction(); - User::find(37978); + User::find(1); DB::commit(); }); @@ -64,12 +64,12 @@ public function cm() public function rl() { DB::beginTransaction(); - $user = User::find(37978); + $user = User::find(1); DB::rollBack(); \sgo(function () { DB::beginTransaction(); - User::find(37978); + User::find(1); DB::rollBack(); }); @@ -87,11 +87,11 @@ public function rl() public function ts2() { DB::connection()->beginTransaction(); - $user = User::find(37978); + $user = User::find(1); \sgo(function () { DB::connection()->beginTransaction(); - User::find(37978); + User::find(1); }); return json_encode($user->toArray()); @@ -108,12 +108,12 @@ public function ts2() public function cm2() { DB::connection()->beginTransaction(); - $user = User::find(37978); + $user = User::find(1); DB::connection()->commit(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(37978); + User::find(1); DB::connection()->commit(); }); @@ -131,12 +131,12 @@ public function cm2() public function rl2() { DB::connection()->beginTransaction(); - $user = User::find(37978); + $user = User::find(1); DB::connection()->rollBack(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(37978); + User::find(1); DB::connection()->rollBack(); }); diff --git a/app/bean.php b/app/bean.php index 5847d226..81f6c0f1 100644 --- a/app/bean.php +++ b/app/bean.php @@ -30,7 +30,7 @@ ], /* @see HttpServer::$setting */ 'setting' => [ - 'task_worker_num' => 4, + 'task_worker_num' => 12, 'task_enable_coroutine' => true ] ], From 1aaae99adee12162e5f376d1413d307eb246a76c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 28 Apr 2019 17:08:57 +0800 Subject: [PATCH 347/643] add --- app/Http/Controller/ViewController.php | 20 ++++++++++++++++++++ app/bean.php | 3 ++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php index b98eccfa..d5e146fb 100644 --- a/app/Http/Controller/ViewController.php +++ b/app/Http/Controller/ViewController.php @@ -32,4 +32,24 @@ public function index(Response $response) $response = $response->withContentType(ContentType::HTML); return $response; } + + /** + * @RequestMapping() + * + * @return array + */ + public function ary(): array + { + return ['ary']; + } + + /** + * @RequestMapping() + * + * @return string + */ + public function str(): string + { + return 'string'; + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 81f6c0f1..69e900d5 100644 --- a/app/bean.php +++ b/app/bean.php @@ -20,7 +20,8 @@ ], 'httpServer' => [ 'class' => HttpServer::class, - 'port' => 18306, +// 'port' => 18306, + 'port' => 88, 'listener' => [ 'rpc' => \bean('rpcServer') ], From 767006492e4530946041b9b06beb0d6145355294 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 28 Apr 2019 17:10:48 +0800 Subject: [PATCH 348/643] Modify port --- app/bean.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/bean.php b/app/bean.php index 69e900d5..81f6c0f1 100644 --- a/app/bean.php +++ b/app/bean.php @@ -20,8 +20,7 @@ ], 'httpServer' => [ 'class' => HttpServer::class, -// 'port' => 18306, - 'port' => 88, + 'port' => 18306, 'listener' => [ 'rpc' => \bean('rpcServer') ], From 3603469bd181537ca449b95e014867e63ecc6e85 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 28 Apr 2019 21:15:52 +0800 Subject: [PATCH 349/643] update some --- .editorconfig | 2 +- composer.json | 1 + resource/views/home/index.php | 174 ++++++++++++++++++++++++++++++++-- 3 files changed, 170 insertions(+), 7 deletions(-) diff --git a/.editorconfig b/.editorconfig index e6d48081..b59a85f8 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,6 +16,6 @@ trim_trailing_whitespace = false [*.php] indent_size = 4 -[resources/views/*.php] +[resource/views/*.php] indent_size = 2 diff --git a/composer.json b/composer.json index 016b035a..6e412448 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "swoft/redis": "^2.0", "swoft/proxy": "^2.0", "swoft/error": "^2.0", + "swoft/view": "dev-master as 2.0", "swoft/component": "dev-master as 2.0" }, "require-dev": { diff --git a/resource/views/home/index.php b/resource/views/home/index.php index 878b88d2..2bd08446 100644 --- a/resource/views/home/index.php +++ b/resource/views/home/index.php @@ -1,13 +1,175 @@ - - - - Swoft Framework + + + + Swoft Framework 2.0 + -

      Hello, world!

      +
      +

      Swoft Framework

      +
      From c63451a1ff9bab548dac3e365c5b5ea3940d8386 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Sun, 28 Apr 2019 22:40:19 +0800 Subject: [PATCH 350/643] add ci --- .travis.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..afe235b7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: php + +php: + - 7.1 + - 7.2 + - 7.3 + +install: + - pecl install -f swoole-4.3.3 +before_script: + - composer update --dev + +script: composer test + +cache: + directories: + - "$HOME/.composer/cache/files" \ No newline at end of file From 71836b3a75b7ebec0e2e62c5915eb972c9bab1d2 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Mon, 29 Apr 2019 12:18:07 +0800 Subject: [PATCH 351/643] add test example --- composer.json | 6 ++++++ dev.composer.json | 6 ++++++ phpunit.xml | 21 +++++++++++++++++++++ test/Cases/ExampleTest.php | 19 +++++++++++++++++++ test/bootstrap.php | 3 ++- 5 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 phpunit.xml create mode 100644 test/Cases/ExampleTest.php diff --git a/composer.json b/composer.json index 016b035a..62805fc4 100644 --- a/composer.json +++ b/composer.json @@ -53,9 +53,15 @@ }, "autoload-dev": { "psr-4": { + "SwoftTest\\": "./test/" } }, "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml", + "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, "repositories": [ { diff --git a/dev.composer.json b/dev.composer.json index d90b7d86..3812367a 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -31,9 +31,15 @@ }, "autoload-dev": { "psr-4": { + "SwoftTest\\": "./test/" } }, "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml", + "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, "repositories": { "packagist": { diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..2e5c0390 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + + ./test + + + + + ./app + + + diff --git a/test/Cases/ExampleTest.php b/test/Cases/ExampleTest.php new file mode 100644 index 00000000..2ecf0adc --- /dev/null +++ b/test/Cases/ExampleTest.php @@ -0,0 +1,19 @@ +assertTrue(true); + } +} \ No newline at end of file diff --git a/test/bootstrap.php b/test/bootstrap.php index 637c614f..7cd4eefb 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,3 +1,4 @@ Date: Mon, 29 Apr 2019 12:39:20 +0800 Subject: [PATCH 352/643] update --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index afe235b7..b01d53c8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,10 @@ language: php php: - - 7.1 - - 7.2 - 7.3 install: - - pecl install -f swoole-4.3.3 + - printf "\n" | pecl install -f swoole-2.1.3 before_script: - composer update --dev From 5311fa938e45362f8d76ac83748c1d6d3d111013 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Mon, 29 Apr 2019 12:45:26 +0800 Subject: [PATCH 353/643] update --- .travis.yml | 2 +- composer.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b01d53c8..6c80f851 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ php: install: - printf "\n" | pecl install -f swoole-2.1.3 before_script: - - composer update --dev + - composer i script: composer test diff --git a/composer.json b/composer.json index 62805fc4..4edf3021 100644 --- a/composer.json +++ b/composer.json @@ -40,8 +40,7 @@ }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5", - "friendsofphp/php-cs-fixer": "^2.10" + "phpunit/phpunit": "^7.5" }, "autoload": { "psr-4": { From 0a7f01141f5a46a645b379606252119cd3162f51 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Mon, 29 Apr 2019 12:56:39 +0800 Subject: [PATCH 354/643] update --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6c80f851..cac78cfe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,6 @@ before_script: script: composer test -cache: - directories: - - "$HOME/.composer/cache/files" \ No newline at end of file +#cache: +# directories: +# - "$HOME/.composer/cache/files" \ No newline at end of file From 6b246b48c0bb4f40341f31d7582c2f02ac385a05 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 30 Apr 2019 19:01:10 +0800 Subject: [PATCH 355/643] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b2df6218..f2d991b0 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,7 @@ ⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole +## Notice + +> The **master** is version `2.0`. If you want to use the 1.x version, please choose the 1.x branch code. + From 33f19ccc10b293bf92f8353c88415c72d812a2b0 Mon Sep 17 00:00:00 2001 From: daydaygo <1252409767@qq.com> Date: Tue, 30 Apr 2019 19:45:49 +0800 Subject: [PATCH 356/643] update composer.json --- composer.json | 72 +++++++++++++++++++++++++---------------------- dev.composer.json | 58 -------------------------------------- 2 files changed, 39 insertions(+), 91 deletions(-) delete mode 100644 dev.composer.json diff --git a/composer.json b/composer.json index 49ae5852..a9aea4c9 100644 --- a/composer.json +++ b/composer.json @@ -12,32 +12,33 @@ "php": ">7.1", "ext-pdo": "*", "ext-json": "*", - "swoft/annotation": "^2.0", - "swoft/bean": "^2.0", - "swoft/event": "^2.0", - "swoft/aop": "^2.0", - "swoft/config": "^2.0", - "swoft/stdlib": "^2.0", - "swoft/framework": "^2.0", - "swoft/http-message": "^2.0", - "swoft/server": "^2.0", - "swoft/tcp-server": "^2.0", - "swoft/http-server": "^2.0", - "swoft/websocket-server": "^2.0", - "swoft/log": "^2.0", - "swoft/db": "^2.0", - "swoft/connection-pool": "^2.0", - "swoft/test": "^2.0", - "swoft/console": "^2.0", - "swoft/rpc": "^2.0", - "swoft/rpc-server": "^2.0", - "swoft/rpc-client": "^2.0", - "swoft/task": "^2.0", - "swoft/redis": "^2.0", - "swoft/proxy": "^2.0", - "swoft/error": "^2.0", - "swoft/view": "dev-master as 2.0", - "swoft/component": "dev-master as 2.0" + "ext-swoole": ">=4.3", + "swoft/annotation": "dev-master", + "swoft/bean": "dev-master", + "swoft/event": "dev-master", + "swoft/aop": "dev-master", + "swoft/config": "dev-master", + "swoft/stdlib": "dev-master", + "swoft/framework": "dev-master", + "swoft/http-message": "dev-master", + "swoft/server": "dev-master", + "swoft/tcp-server": "dev-master", + "swoft/http-server": "dev-master", + "swoft/websocket-server": "dev-master", + "swoft/log": "dev-master", + "swoft/db": "dev-master", + "swoft/connection-pool": "dev-master", + "swoft/test": "dev-master", + "swoft/console": "dev-master", + "swoft/rpc": "dev-master", + "swoft/rpc-server": "dev-master", + "swoft/rpc-client": "dev-master", + "swoft/task": "dev-master", + "swoft/redis": "dev-master", + "swoft/proxy": "dev-master", + "swoft/error": "dev-master", + "swoft/view": "dev-master", + "swoft/devtool": "dev-master" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", @@ -56,6 +57,7 @@ "SwoftTest\\": "./test/" } }, + "minimum-stability": "dev", "scripts": { "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" @@ -63,14 +65,18 @@ "test": "./vendor/bin/phpunit -c phpunit.xml", "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, - "repositories": [ - { - "type": "git", - "url": "git@github.com:swoft-cloud/swoft-component.git" - }, - { + "repositories": { + "packagist": { "type": "composer", "url": "https://packagist.laravel-china.org/" + }, + "swoft": { + "type": "path", + "url": "../swoft-component/src/*" + }, + "swoft/devtool": { + "type": "path", + "url": "../swoft-devtool" } - ] + } } diff --git a/dev.composer.json b/dev.composer.json deleted file mode 100644 index b73a5314..00000000 --- a/dev.composer.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "name": "swoft/swoft", - "type": "project", - "keywords": [ - "php", - "swoole", - "swoft" - ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", - "license": "Apache-2.0", - "require": { - "php": ">7.1", - "ext-pdo": "*", - "ext-json": "*", - "ext-redis": "*", - "swoft/view": "dev-master", - "swoft/devtool": "dev-master", - "swoft/component": "dev-master as 2.0" - }, - "require-dev": { - "phpunit/phpunit": "^7.5", - "swoft/swoole-ide-helper": "dev-master" - }, - "autoload": { - "psr-4": { - "App\\": "app/" - }, - "files": [ - "app/Helper/Functions.php" - ] - }, - "autoload-dev": { - "psr-4": { - "SwoftTest\\": "./test/" - } - }, - "scripts": { - "post-root-package-install": [ - "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" - ], - "test": "./vendor/bin/phpunit -c phpunit.xml", - "cs-fix": "./vendor/bin/php-cs-fixer fix $1" - }, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - }, - "0": { - "type": "path", - "url": "../swoft-component" - }, - "2": { - "type": "path", - "url": "../swoft-devtool" - } - } -} From 29cedf05a2e31eeb3294f43dadf6c453d32793f2 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 1 May 2019 07:33:51 +0800 Subject: [PATCH 357/643] add composer --- composer.json | 45 +++++--------------------- dev.composer.json | 82 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 dev.composer.json diff --git a/composer.json b/composer.json index a9aea4c9..574a7c51 100644 --- a/composer.json +++ b/composer.json @@ -12,33 +12,8 @@ "php": ">7.1", "ext-pdo": "*", "ext-json": "*", - "ext-swoole": ">=4.3", - "swoft/annotation": "dev-master", - "swoft/bean": "dev-master", - "swoft/event": "dev-master", - "swoft/aop": "dev-master", - "swoft/config": "dev-master", - "swoft/stdlib": "dev-master", - "swoft/framework": "dev-master", - "swoft/http-message": "dev-master", - "swoft/server": "dev-master", - "swoft/tcp-server": "dev-master", - "swoft/http-server": "dev-master", - "swoft/websocket-server": "dev-master", - "swoft/log": "dev-master", - "swoft/db": "dev-master", - "swoft/connection-pool": "dev-master", - "swoft/test": "dev-master", - "swoft/console": "dev-master", - "swoft/rpc": "dev-master", - "swoft/rpc-server": "dev-master", - "swoft/rpc-client": "dev-master", - "swoft/task": "dev-master", - "swoft/redis": "dev-master", - "swoft/proxy": "dev-master", - "swoft/error": "dev-master", - "swoft/view": "dev-master", - "swoft/devtool": "dev-master" + "swoft/view": "dev-master as 2.0", + "swoft/component": "dev-master as 2.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", @@ -65,18 +40,14 @@ "test": "./vendor/bin/phpunit -c phpunit.xml", "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, - "repositories": { - "packagist": { + "repositories": [ + { "type": "composer", "url": "https://packagist.laravel-china.org/" }, - "swoft": { - "type": "path", - "url": "../swoft-component/src/*" - }, - "swoft/devtool": { - "type": "path", - "url": "../swoft-devtool" + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-component.git" } - } + ] } diff --git a/dev.composer.json b/dev.composer.json new file mode 100644 index 00000000..a9aea4c9 --- /dev/null +++ b/dev.composer.json @@ -0,0 +1,82 @@ +{ + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", + "license": "Apache-2.0", + "require": { + "php": ">7.1", + "ext-pdo": "*", + "ext-json": "*", + "ext-swoole": ">=4.3", + "swoft/annotation": "dev-master", + "swoft/bean": "dev-master", + "swoft/event": "dev-master", + "swoft/aop": "dev-master", + "swoft/config": "dev-master", + "swoft/stdlib": "dev-master", + "swoft/framework": "dev-master", + "swoft/http-message": "dev-master", + "swoft/server": "dev-master", + "swoft/tcp-server": "dev-master", + "swoft/http-server": "dev-master", + "swoft/websocket-server": "dev-master", + "swoft/log": "dev-master", + "swoft/db": "dev-master", + "swoft/connection-pool": "dev-master", + "swoft/test": "dev-master", + "swoft/console": "dev-master", + "swoft/rpc": "dev-master", + "swoft/rpc-server": "dev-master", + "swoft/rpc-client": "dev-master", + "swoft/task": "dev-master", + "swoft/redis": "dev-master", + "swoft/proxy": "dev-master", + "swoft/error": "dev-master", + "swoft/view": "dev-master", + "swoft/devtool": "dev-master" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^7.5" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Helper/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "SwoftTest\\": "./test/" + } + }, + "minimum-stability": "dev", + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml", + "cs-fix": "./vendor/bin/php-cs-fixer fix $1" + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.laravel-china.org/" + }, + "swoft": { + "type": "path", + "url": "../swoft-component/src/*" + }, + "swoft/devtool": { + "type": "path", + "url": "../swoft-devtool" + } + } +} From 419e74daf96f3bc7e503e647a154b08685dacd84 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 2 May 2019 00:25:25 +0800 Subject: [PATCH 358/643] update: add public dir --- public/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/.keep diff --git a/public/.keep b/public/.keep new file mode 100644 index 00000000..e69de29b From c5eb4ae3bb5e21cd9d5eff1581ebe952d0335348 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 15 May 2019 19:28:56 +0800 Subject: [PATCH 359/643] add demo --- app/Console/Command/TestCommand.php | 1 + app/Http/Controller/DbModelController.php | 22 +++++++++++++++++ .../Controller/DbTransactionController.php | 24 +++++++++---------- app/Http/Controller/LogController.php | 5 ++-- app/bean.php | 2 +- composer.json | 9 +++---- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 7dca9f3f..89a49488 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -67,6 +67,7 @@ private function uris(): array '/dbTransaction/ts2', '/dbTransaction/cm2', '/dbTransaction/rl2', + '/dbModel/find' ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index d366e71a..2bc1400d 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -3,8 +3,30 @@ namespace App\Http\Controller; +use App\Model\Entity\User; +use Swoft\Http\Server\Annotation\Mapping\Controller; +use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +/** + * Class DbModelController + * + * @since 2.0 + * + * @Controller(prefix="dbModel") + */ class DbModelController { + /** + * @RequestMapping(route="find") + * + * @return array + * + * @throws \Exception + */ + public function find(): array + { + $user = User::find(22); + return $user->toArray(); + } } \ No newline at end of file diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 8fdbab2e..e15bc5bd 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -26,11 +26,11 @@ class DbTransactionController public function ts() { DB::beginTransaction(); - $user = User::find(1); + $user = User::find(22); \sgo(function () { DB::beginTransaction(); - User::find(1); + User::find(22); }); return json_encode($user->toArray()); @@ -44,12 +44,12 @@ public function ts() public function cm() { DB::beginTransaction(); - $user = User::find(1); + $user = User::find(22); DB::commit(); \sgo(function () { DB::beginTransaction(); - User::find(1); + User::find(22); DB::commit(); }); @@ -64,12 +64,12 @@ public function cm() public function rl() { DB::beginTransaction(); - $user = User::find(1); + $user = User::find(22); DB::rollBack(); \sgo(function () { DB::beginTransaction(); - User::find(1); + User::find(22); DB::rollBack(); }); @@ -87,11 +87,11 @@ public function rl() public function ts2() { DB::connection()->beginTransaction(); - $user = User::find(1); + $user = User::find(22); \sgo(function () { DB::connection()->beginTransaction(); - User::find(1); + User::find(22); }); return json_encode($user->toArray()); @@ -108,12 +108,12 @@ public function ts2() public function cm2() { DB::connection()->beginTransaction(); - $user = User::find(1); + $user = User::find(22); DB::connection()->commit(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(1); + User::find(22); DB::connection()->commit(); }); @@ -131,12 +131,12 @@ public function cm2() public function rl2() { DB::connection()->beginTransaction(); - $user = User::find(1); + $user = User::find(22); DB::connection()->rollBack(); \sgo(function () { DB::connection()->beginTransaction(); - User::find(1); + User::find(22); DB::connection()->rollBack(); }); diff --git a/app/Http/Controller/LogController.php b/app/Http/Controller/LogController.php index 5b11cd00..460fc6d9 100644 --- a/app/Http/Controller/LogController.php +++ b/app/Http/Controller/LogController.php @@ -53,9 +53,8 @@ public function test(): array // Tag2 end Log::profileEnd('tagName'); - // Counting - Log::counting('mget', 1, 10); - Log::counting('mget', 2, 10); + + return ['log']; } diff --git a/app/bean.php b/app/bean.php index c5ff5118..4b72c4a5 100644 --- a/app/bean.php +++ b/app/bean.php @@ -36,7 +36,7 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.1', + 'dsn' => 'mysql:dbname=test;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', ], diff --git a/composer.json b/composer.json index 574a7c51..46355666 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "php": ">7.1", "ext-pdo": "*", "ext-json": "*", + "ext-mbstring": "*", "swoft/view": "dev-master as 2.0", "swoft/component": "dev-master as 2.0" }, @@ -41,13 +42,13 @@ "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, "repositories": [ - { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - }, { "type": "git", "url": "git@github.com:swoft-cloud/swoft-component.git" + }, + { + "type": "composer", + "url": "https://packagist.laravel-china.org/" } ] } From 0ea9daec0911e192157b5864bd91ffba4d664a3e Mon Sep 17 00:00:00 2001 From: ccinn <471113744@qq.com> Date: Thu, 16 May 2019 15:12:08 +0800 Subject: [PATCH 360/643] Update composer.json Component's pull does not take SSH protocol, modify the component to take HTTPS protocol --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 574a7c51..a8562382 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ }, { "type": "git", - "url": "git@github.com:swoft-cloud/swoft-component.git" + "url": "https://github.com/swoft-cloud/swoft-component.git" } ] } From b5d1b2d11841f23ee44de7c9a5eea1df6ae6e1a4 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 16 May 2019 20:37:26 +0800 Subject: [PATCH 361/643] Fix bug --- app/Console/Command/TestCommand.php | 14 ++-- .../Handler/HttpExceptionHandler.php | 22 +++-- app/Http/Controller/CoController.php | 6 +- app/Http/Controller/DbModelController.php | 21 ++++- .../Controller/DbTransactionController.php | 81 ++++++++++++------- app/Http/Controller/HomeController.php | 23 ++++-- app/Http/Controller/LogController.php | 6 +- app/Http/Controller/RedisController.php | 19 +++-- app/Http/Controller/TaskController.php | 9 ++- app/Http/Controller/ViewController.php | 6 +- app/Task/Listener/FinishListener.php | 3 +- app/WebSocket/ChatModule.php | 3 +- app/bean.php | 12 +-- 13 files changed, 149 insertions(+), 76 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 89a49488..b3f1d60b 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -3,6 +3,9 @@ namespace App\Console\Command; +use function input; +use function output; +use function sprintf; use Swoft\Console\Annotation\Mapping\Command; use Swoft\Console\Annotation\Mapping\CommandMapping; @@ -20,7 +23,7 @@ class TestCommand */ public function ab() { - $type = \input()->get('type', ''); + $type = input()->get('type', ''); $uris = $this->uris(); // Format data @@ -34,12 +37,12 @@ public function ab() } foreach ($exeUris as $uri) { - $abShell = \sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); - $curlShell = \sprintf('curl 127.0.0.1:18306%s', $uri); + $abShell = sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); exec($curlShell, $curlResult); - \output()->writeln('执行结果:' . json_encode($curlResult)); - \output()->writeln('执行URL:' . $abShell . PHP_EOL); + output()->writeln('执行结果:' . json_encode($curlResult)); + output()->writeln('执行URL:' . $abShell . PHP_EOL); exec($abShell, $abResult); } @@ -61,6 +64,7 @@ private function uris(): array '/log/test' ], 'db' => [ + '/dbModel/save', '/dbTransaction/ts', '/dbTransaction/cm', '/dbTransaction/rl', diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 65e23ddd..ff272f82 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -2,9 +2,15 @@ namespace App\Exception\Handler; +use const APP_DEBUG; +use function get_class; +use ReflectionException; +use function sprintf; +use Swoft\Bean\Exception\ContainerException; use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Http\Message\Response; use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; +use Throwable; /** * Class HttpExceptionHandler @@ -14,26 +20,26 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler { /** - * @param \Throwable $e + * @param Throwable $e * @param Response $response * * @return Response - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException + * @throws ReflectionException + * @throws ContainerException */ - public function handle(\Throwable $e, Response $response): Response + public function handle(Throwable $e, Response $response): Response { // Debug is false - if (!\APP_DEBUG) { + if (!APP_DEBUG) { return $response->withStatus(500)->withContent( - \sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()) + sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()) ); } $data = [ 'code' => $e->getCode(), - 'error' => \sprintf('(%s) %s', \get_class($e), $e->getMessage()), - 'file' => \sprintf('At %s line %d', $e->getFile(), $e->getLine()), + 'error' => sprintf('(%s) %s', get_class($e), $e->getMessage()), + 'file' => sprintf('At %s line %d', $e->getFile(), $e->getLine()), 'trace' => $e->getTraceAsString(), ]; diff --git a/app/Http/Controller/CoController.php b/app/Http/Controller/CoController.php index abdb854b..f78912cc 100644 --- a/app/Http/Controller/CoController.php +++ b/app/Http/Controller/CoController.php @@ -4,11 +4,13 @@ namespace App\Http\Controller; use App\Model\Entity\User; +use Exception; use Swoft\Co; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Redis\Redis; use Swoole\Coroutine\Http\Client; +use Throwable; /** * Class CoController @@ -24,7 +26,7 @@ class CoController * * @return array * - * @throws \Exception + * @throws Exception */ public function multi(): array { @@ -58,7 +60,7 @@ public static function getUser(): array /** * @return array - * @throws \Throwable + * @throws Throwable */ public function addUser(): array { diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 2bc1400d..80811d9b 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -4,6 +4,7 @@ namespace App\Http\Controller; use App\Model\Entity\User; +use Exception; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; @@ -21,7 +22,7 @@ class DbModelController * * @return array * - * @throws \Exception + * @throws Exception */ public function find(): array { @@ -29,4 +30,22 @@ public function find(): array return $user->toArray(); } + + /** + * @RequestMapping(route="save") + * + * @return array + * + * @throws Exception + */ + public function save(): array + { + $user = new User(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + + return $user->toArray(); + } } \ No newline at end of file diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index e15bc5bd..13f6b006 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -4,10 +4,11 @@ namespace App\Http\Controller; use App\Model\Entity\User; -use mysql_xdevapi\Exception; use Swoft\Db\DB; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Throwable; +use function sgo; /** * Class DbTransactionController @@ -22,15 +23,18 @@ class DbTransactionController * @RequestMapping(route="ts") * * @return false|string + * @throws Throwable */ public function ts() { + $id = $this->getId(); + DB::beginTransaction(); - $user = User::find(22); + $user = User::find($id); - \sgo(function () { + sgo(function () use ($id) { DB::beginTransaction(); - User::find(22); + User::find($id); }); return json_encode($user->toArray()); @@ -40,16 +44,19 @@ public function ts() * @RequestMapping(route="cm") * * @return false|string + * @throws Throwable */ public function cm() { + $id = $this->getId(); + DB::beginTransaction(); - $user = User::find(22); + $user = User::find($id); DB::commit(); - \sgo(function () { + sgo(function () use ($id) { DB::beginTransaction(); - User::find(22); + User::find($id); DB::commit(); }); @@ -60,16 +67,19 @@ public function cm() * @RequestMapping(route="rl") * * @return false|string + * @throws Throwable */ public function rl() { + $id = $this->getId(); + DB::beginTransaction(); - $user = User::find(22); + $user = User::find($id); DB::rollBack(); - \sgo(function () { + sgo(function () use ($id) { DB::beginTransaction(); - User::find(22); + User::find($id); DB::rollBack(); }); @@ -80,18 +90,18 @@ public function rl() * @RequestMapping(route="ts2") * * @return false|string - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException - * @throws \Swoft\Db\Exception\PoolException + * @throws Throwable */ public function ts2() { + $id = $this->getId(); + DB::connection()->beginTransaction(); - $user = User::find(22); + $user = User::find($id); - \sgo(function () { + sgo(function () use ($id) { DB::connection()->beginTransaction(); - User::find(22); + User::find($id); }); return json_encode($user->toArray()); @@ -101,19 +111,19 @@ public function ts2() * @RequestMapping(route="cm2") * * @return false|string - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException - * @throws \Swoft\Db\Exception\PoolException + * @throws Throwable */ public function cm2() { + $id = $this->getId(); + DB::connection()->beginTransaction(); - $user = User::find(22); + $user = User::find($id); DB::connection()->commit(); - \sgo(function () { + sgo(function () use ($id) { DB::connection()->beginTransaction(); - User::find(22); + User::find($id); DB::connection()->commit(); }); @@ -124,22 +134,37 @@ public function cm2() * @RequestMapping(route="rl2") * * @return false|string - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException - * @throws \Swoft\Db\Exception\PoolException + * @throws Throwable */ public function rl2() { + $id = $this->getId(); + DB::connection()->beginTransaction(); - $user = User::find(22); + $user = User::find($id); DB::connection()->rollBack(); - \sgo(function () { + sgo(function () use ($id) { DB::connection()->beginTransaction(); - User::find(22); + User::find($id); DB::connection()->rollBack(); }); return json_encode($user->toArray()); } + + /** + * @return int + * @throws Throwable + */ + public function getId(): int + { + $user = new User(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + + return $user->getId(); + } } \ No newline at end of file diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 0b190b5a..560d0cb3 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -2,12 +2,19 @@ namespace App\Http\Controller; +use const E_USER_ERROR; +use ReflectionException; +use RuntimeException; +use Swoft; +use Swoft\Bean\Exception\ContainerException; use Swoft\Context\Context; use Swoft\Http\Message\ContentType; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\View\Renderer; +use Throwable; +use function trigger_error; /** * Class HomeController @@ -17,12 +24,12 @@ class HomeController { /** * @RequestMapping("/") - * @throws \Throwable + * @throws Throwable */ public function index(): Response { /** @var Renderer $renderer */ - $renderer = \Swoft::getBean('view'); + $renderer = Swoft::getBean('view'); $content = $renderer->render('home/index'); return Context::mustGet() @@ -35,8 +42,8 @@ public function index(): Response * @RequestMapping("/hello[/{name}]") * @param string $name * @return Response - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException + * @throws ReflectionException + * @throws ContainerException */ public function hello(string $name): Response { @@ -47,19 +54,19 @@ public function hello(string $name): Response /** * @RequestMapping("/ex") - * @throws \Throwable + * @throws Throwable */ public function ex(): void { - throw new \RuntimeException('exception throw on ' . __METHOD__); + throw new RuntimeException('exception throw on ' . __METHOD__); } /** * @RequestMapping("/er") - * @throws \Throwable + * @throws Throwable */ public function er(): void { - \trigger_error('user error', \E_USER_ERROR); + trigger_error('user error', E_USER_ERROR); } } diff --git a/app/Http/Controller/LogController.php b/app/Http/Controller/LogController.php index 460fc6d9..aedcb8d2 100644 --- a/app/Http/Controller/LogController.php +++ b/app/Http/Controller/LogController.php @@ -3,6 +3,8 @@ namespace App\Http\Controller; +use ReflectionException; +use Swoft\Bean\Exception\ContainerException; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Log\Helper\Log; @@ -20,8 +22,8 @@ class LogController * @RequestMapping("test") * * @return array - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException + * @throws ReflectionException + * @throws ContainerException */ public function test(): array { diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index 41e17a87..9aa0b081 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -3,8 +3,11 @@ namespace App\Http\Controller; +use Exception; +use function sgo; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\Redis\Exception\RedisException; use Swoft\Redis\Redis; /** @@ -39,11 +42,11 @@ public function str(): array * @RequestMapping("release") * * @return array - * @throws \Swoft\Redis\Exception\RedisException + * @throws RedisException */ public function release(): array { - \sgo(function () { + sgo(function () { Redis::connection(); }); @@ -61,14 +64,14 @@ public function release(): array */ public function exPipeline(): array { - \sgo(function () { + sgo(function () { Redis::pipeline(function () { - throw new \Exception(''); + throw new Exception(''); }); }); Redis::pipeline(function () { - throw new \Exception(''); + throw new Exception(''); }); return ['exPipeline']; @@ -83,14 +86,14 @@ public function exPipeline(): array */ public function exTransaction(): array { - \sgo(function () { + sgo(function () { Redis::transaction(function () { - throw new \Exception(''); + throw new Exception(''); }); }); Redis::transaction(function () { - throw new \Exception(''); + throw new Exception(''); }); return ['exPipeline']; diff --git a/app/Http/Controller/TaskController.php b/app/Http/Controller/TaskController.php index d6866bc1..b3f4f17f 100644 --- a/app/Http/Controller/TaskController.php +++ b/app/Http/Controller/TaskController.php @@ -5,6 +5,7 @@ use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\Task\Exception\TaskException; use Swoft\Task\Task; /** @@ -20,7 +21,7 @@ class TaskController * @RequestMapping() * * @return array - * @throws \Swoft\Task\Exception\TaskException + * @throws TaskException */ public function getListByCo(): array { @@ -33,7 +34,7 @@ public function getListByCo(): array * @RequestMapping(route="deleteByCo") * * @return array - * @throws \Swoft\Task\Exception\TaskException + * @throws TaskException */ public function deleteByCo(): array { @@ -49,7 +50,7 @@ public function deleteByCo(): array * @RequestMapping() * * @return array - * @throws \Swoft\Task\Exception\TaskException + * @throws TaskException */ public function getListByAsync(): array { @@ -62,7 +63,7 @@ public function getListByAsync(): array * @RequestMapping(route="deleteByAsync") * * @return array - * @throws \Swoft\Task\Exception\TaskException + * @throws TaskException */ public function deleteByAsync(): array { diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php index d5e146fb..213b25ea 100644 --- a/app/Http/Controller/ViewController.php +++ b/app/Http/Controller/ViewController.php @@ -3,6 +3,8 @@ namespace App\Http\Controller; +use ReflectionException; +use Swoft\Bean\Exception\ContainerException; use Swoft\Http\Message\ContentType; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; @@ -23,8 +25,8 @@ class ViewController * @param Response $response * * @return Response - * @throws \ReflectionException - * @throws \Swoft\Bean\Exception\ContainerException + * @throws ReflectionException + * @throws ContainerException */ public function index(Response $response) { diff --git a/app/Task/Listener/FinishListener.php b/app/Task/Listener/FinishListener.php index 061991f5..9d899db2 100644 --- a/app/Task/Listener/FinishListener.php +++ b/app/Task/Listener/FinishListener.php @@ -3,6 +3,7 @@ namespace App\Task\Listener; +use function context; use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; @@ -23,6 +24,6 @@ class FinishListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - CLog::info(\context()->getTaskUniqid()); + CLog::info(context()->getTaskUniqid()); } } \ No newline at end of file diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index 620a8531..69d73d6a 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -3,6 +3,7 @@ namespace App\WebSocket; use App\WebSocket\Chat\HomeController; +use function server; use Swoft\Http\Message\Request; use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen; use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; @@ -26,6 +27,6 @@ class ChatModule */ public function onOpen(Request $request, int $fd): void { - \server()->push($request->getFd(), "Opened, welcome!(FD: $fd)"); + server()->push($request->getFd(), "Opened, welcome!(FD: $fd)"); } } diff --git a/app/bean.php b/app/bean.php index 4b72c4a5..d089bc40 100644 --- a/app/bean.php +++ b/app/bean.php @@ -22,11 +22,11 @@ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ - 'rpc' => \bean('rpcServer') + 'rpc' => bean('rpcServer') ], 'on' => [ - SwooleEvent::TASK => \bean(TaskListener::class), // Enable task must task and finish event - SwooleEvent::FINISH => \bean(FinishListener::class) + SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event + SwooleEvent::FINISH => bean(FinishListener::class) ], /* @see HttpServer::$setting */ 'setting' => [ @@ -56,11 +56,11 @@ 'write_timeout' => 10.0, 'read_timeout' => 0.5, ], - 'packet' => \bean('rpcClientPacket') + 'packet' => bean('rpcClientPacket') ], 'user.pool' => [ 'class' => ServicePool::class, - 'client' => \bean('user') + 'client' => bean('user') ], 'rpcServer' => [ 'class' => ServiceServer::class, @@ -69,7 +69,7 @@ 'class' => WebSocketServer::class, 'on' => [ // Enable http handle - SwooleEvent::REQUEST => \bean(RequestListener::class), + SwooleEvent::REQUEST => bean(RequestListener::class), ], 'debug' => env('SWOFT_DEBUG', 0), /* @see WebSocketServer::$setting */ From eadde6dbd5c9150da0f2a320f1c024bb0a351c5b Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 16 May 2019 20:44:53 +0800 Subject: [PATCH 362/643] add --- app/Console/Command/TestCommand.php | 6 ++- app/Http/Controller/DbModelController.php | 54 ++++++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index b3f1d60b..852e1374 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -64,14 +64,16 @@ private function uris(): array '/log/test' ], 'db' => [ - '/dbModel/save', '/dbTransaction/ts', '/dbTransaction/cm', '/dbTransaction/rl', '/dbTransaction/ts2', '/dbTransaction/cm2', '/dbTransaction/rl2', - '/dbModel/find' + '/dbModel/find', + '/dbModel/update', + '/dbModel/delete', + '/dbModel/save', ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 80811d9b..ba247d39 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -7,6 +7,7 @@ use Exception; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Throwable; /** * Class DbModelController @@ -22,11 +23,12 @@ class DbModelController * * @return array * - * @throws Exception + * @throws Throwable */ public function find(): array { - $user = User::find(22); + $id = $this->getId(); + $user = User::find($id); return $user->toArray(); } @@ -48,4 +50,52 @@ public function save(): array return $user->toArray(); } + + /** + * @RequestMapping(route="update") + * + * @return array + * + * @throws Throwable + */ + public function update(): array + { + $id = $this->getId(); + + User::updateOrInsert(['id'=>$id], ['name'=> 'swoft']); + + $user = User::find($id); + + return $user->toArray(); + } + + /** + * @RequestMapping(route="delete") + * + * @return array + * + * @throws Throwable + */ + public function delete(): array + { + $id = $this->getId(); + $result = User::find($id)->delete(); + + return [$result]; + } + + /** + * @return int + * @throws Throwable + */ + public function getId(): int + { + $user = new User(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + + return $user->getId(); + } } \ No newline at end of file From 9dd917d98d722cbae8a51a1a0571fad9b906a9fa Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 17 May 2019 21:50:37 +0800 Subject: [PATCH 363/643] Added redis --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 46355666..b61c5439 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "require": { "php": ">7.1", "ext-pdo": "*", + "ext-redis": "*", "ext-json": "*", "ext-mbstring": "*", "swoft/view": "dev-master as 2.0", From ad1036d4e03e3dd82cd7771e06ad8473a950c326 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 19 May 2019 11:53:10 +0800 Subject: [PATCH 364/643] add ext mbstring to composer --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 574a7c51..cc16e9af 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "php": ">7.1", "ext-pdo": "*", "ext-json": "*", + "ext-mbstring": "*", "swoft/view": "dev-master as 2.0", "swoft/component": "dev-master as 2.0" }, From b8cc1b3c81ea62b4492f0473545f85c6a8fdd7ca Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 19 May 2019 11:56:33 +0800 Subject: [PATCH 365/643] format codes --- app/Application.php | 2 +- app/Console/Command/DemoCommand.php | 2 +- app/Console/Command/TestCommand.php | 12 ++++++------ app/WebSocket/Chat/HomeController.php | 8 +++++--- app/WebSocket/ChatModule.php | 2 +- app/WebSocket/EchoModule.php | 3 ++- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/Application.php b/app/Application.php index 2ec069e3..d68e92c6 100644 --- a/app/Application.php +++ b/app/Application.php @@ -14,4 +14,4 @@ class Application extends SwoftApplication { -} \ No newline at end of file +} diff --git a/app/Console/Command/DemoCommand.php b/app/Console/Command/DemoCommand.php index a535d55a..7cdd930d 100644 --- a/app/Console/Command/DemoCommand.php +++ b/app/Console/Command/DemoCommand.php @@ -9,7 +9,7 @@ /** * Class DemoCommand - * @package App\Console\Command + * * @Command() */ class DemoCommand diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 852e1374..03502997 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -3,11 +3,11 @@ namespace App\Console\Command; +use Swoft\Console\Annotation\Mapping\Command; +use Swoft\Console\Annotation\Mapping\CommandMapping; use function input; use function output; use function sprintf; -use Swoft\Console\Annotation\Mapping\Command; -use Swoft\Console\Annotation\Mapping\CommandMapping; /** * Class TestCommand @@ -75,20 +75,20 @@ private function uris(): array '/dbModel/delete', '/dbModel/save', ], - 'task' => [ + 'task' => [ '/task/getListByCo', '/task/deleteByCo', '/task/getListByAsync', '/task/deleteByAsync', ], - 'rpc' => [ + 'rpc' => [ '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', ], - 'co' => [ + 'co' => [ '/co/multi' ] ]; } -} \ No newline at end of file +} diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php index 3ec7a46a..ef2ac788 100644 --- a/app/WebSocket/Chat/HomeController.php +++ b/app/WebSocket/Chat/HomeController.php @@ -3,11 +3,12 @@ namespace App\WebSocket\Chat; use Swoft\Session\Session; -use Swoft\WebSocket\Server\Annotation\Mapping\WsController; use Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping; +use Swoft\WebSocket\Server\Annotation\Mapping\WsController; /** * Class HomeController + * * @WsController() */ class HomeController @@ -31,7 +32,7 @@ public function index(): void */ public function echo($data): void { - Session::mustGet()->push('(home.echo)Recv: ' .$data); + Session::mustGet()->push('(home.echo)Recv: ' . $data); } /** @@ -39,10 +40,11 @@ public function echo($data): void * * @param $data * @MessageMapping("ar") + * * @return string */ public function autoReply($data): string { - return'(home.ar)Recv: ' .$data; + return '(home.ar)Recv: ' . $data; } } diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index 69d73d6a..07cd3177 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -3,11 +3,11 @@ namespace App\WebSocket; use App\WebSocket\Chat\HomeController; -use function server; use Swoft\Http\Message\Request; use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen; use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; use Swoft\WebSocket\Server\MessageParser\TokenTextParser; +use function server; /** * Class ChatModule diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php index bce32a51..0e1dc80a 100644 --- a/app/WebSocket/EchoModule.php +++ b/app/WebSocket/EchoModule.php @@ -8,6 +8,7 @@ use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; use Swoole\WebSocket\Frame; use Swoole\WebSocket\Server; +use function server; /** * Class EchoModule @@ -23,7 +24,7 @@ class EchoModule */ public function onOpen(Request $request, int $fd): void { - \server()->push($request->getFd(), "Opened, welcome!(FD: $fd)"); + server()->push($request->getFd(), "Opened, welcome!(FD: $fd)"); } /** From 7378ee087a0a740def7d6db8e450386db82873e8 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 20 May 2019 23:12:06 +0800 Subject: [PATCH 366/643] add --- .travis.yml | 19 ++++++++++++------- test/Cases/ExampleTest.php | 7 ------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index cac78cfe..07ca29df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,20 @@ language: php php: + - 7.1 + - 7.2 - 7.3 - +services: + - redis install: - - printf "\n" | pecl install -f swoole-2.1.3 + - echo 'no' | pecl install -f redis + - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - + - echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + before_script: - - composer i + - composer config -g process-timeout 900 && composer update + - phpenv config-rm xdebug.ini -script: composer test -#cache: -# directories: -# - "$HOME/.composer/cache/files" \ No newline at end of file +script: + - composer test diff --git a/test/Cases/ExampleTest.php b/test/Cases/ExampleTest.php index 2ecf0adc..4f490c50 100644 --- a/test/Cases/ExampleTest.php +++ b/test/Cases/ExampleTest.php @@ -1,11 +1,4 @@ Date: Tue, 21 May 2019 01:19:28 +0800 Subject: [PATCH 367/643] Modify composer --- LICENSE | 201 +++++++++++++++++++++++++++++++++++++ README.md | 37 ++++++- composer.json | 31 +++--- test/Cases/ExampleTest.php | 12 --- 4 files changed, 252 insertions(+), 29 deletions(-) create mode 100644 LICENSE delete mode 100644 test/Cases/ExampleTest.php diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index f2d991b0..2926a50d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,43 @@ -# Swoft +

      + + < img src="http://qiniu.daydaygo.top/swoft-logo.png?imageView2/2/w/300" alt="swoft" /> + +

      + +[![Latest Version](https://img.shields.io/badge/version-v2.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) +[![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) +[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) ⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole +## Feature + +- Built-in high performance network server(Http/Websocket/RPC) +- Flexible componentization +- Flexible annotation function +- Diversified command terminal(Console) +- Powerful Aspect Oriented Programming(AOP) +- Perfect Container management、Dependency Injection (DI) +- Flexible event mechanism +- Implementation of HTTP message based on PSR-7 +- Event Manager Based on PSR-14 +- Middleware based on PSR-15 +- Internationalization(i18n) support +- Simple and efficient parameter validator +- High performance connection pool(Mysql/Redis/RPC),Automatic reconnection +- Database is highly compatible Laravel +- Cache Redis highly compatible Laravel +- Efficient task processing +- Flexible exception handling +- Powerful log system + ## Notice > The **master** is version `2.0`. If you want to use the 1.x version, please choose the 1.x branch code. +## License + +Swoft is an open-source software licensed under the [LICENSE](LICENSE) \ No newline at end of file diff --git a/composer.json b/composer.json index 71f82470..6434976a 100644 --- a/composer.json +++ b/composer.json @@ -14,12 +14,21 @@ "ext-redis": "*", "ext-json": "*", "ext-mbstring": "*", - "swoft/view": "dev-master as 2.0", - "swoft/component": "dev-master as 2.0" + "swoft/view": "~2.0.0", + "swoft/framework": "~2.0.0", + "swoft/db": "~2.0.0", + "swoft/http-server": "~2.0.0", + "swoft/i18n": "~2.0.0", + "swoft/redis": "~2.0.0", + "swoft/rpc-client": "~2.0.0", + "swoft/rpc-server": "~2.0.0", + "swoft/task": "~2.0.0", + "swoft/websocket-server": "~2.0.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^7.5", + "swoft/devtool": "~2.0.0" }, "autoload": { "psr-4": { @@ -31,25 +40,15 @@ }, "autoload-dev": { "psr-4": { - "SwoftTest\\": "./test/" + "App\\Testing\\": "test/testing", + "App\\Unit\\": "test/unit" } }, - "minimum-stability": "dev", "scripts": { "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "test": "./vendor/bin/phpunit -c phpunit.xml", "cs-fix": "./vendor/bin/php-cs-fixer fix $1" - }, - "repositories": [ - { - "type": "git", - "url": "https://github.com/swoft-cloud/swoft-component.git" - }, - { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - } - ] + } } diff --git a/test/Cases/ExampleTest.php b/test/Cases/ExampleTest.php deleted file mode 100644 index 4f490c50..00000000 --- a/test/Cases/ExampleTest.php +++ /dev/null @@ -1,12 +0,0 @@ -assertTrue(true); - } -} \ No newline at end of file From 91bc11e6147e5a9510ca611b1f41bf0f3313cf2b Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 21 May 2019 01:22:03 +0800 Subject: [PATCH 368/643] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2926a50d..d6294efe 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@

      - - < img src="http://qiniu.daydaygo.top/swoft-logo.png?imageView2/2/w/300" alt="swoft" /> - -

      + + swoft + +

      [![Latest Version](https://img.shields.io/badge/version-v2.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) @@ -40,4 +40,4 @@ ## License -Swoft is an open-source software licensed under the [LICENSE](LICENSE) \ No newline at end of file +Swoft is an open-source software licensed under the [LICENSE](LICENSE) From b60e1ed14d6e584af75d4dc983be3a8bef6f11f3 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 21 May 2019 01:22:58 +0800 Subject: [PATCH 369/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6294efe..80adccb7 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) -[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://doc.swoft.org) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) ⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole From 288995b397ecb3efad8f68cf67fbb324d1500b2e Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 21 May 2019 02:05:17 +0800 Subject: [PATCH 370/643] Modify composer --- app/bean.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bean.php b/app/bean.php index d089bc40..f3492abe 100644 --- a/app/bean.php +++ b/app/bean.php @@ -36,7 +36,7 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.3', + 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', ], From 24a0869551d4e7bf662a7881737f6800cf35fe5c Mon Sep 17 00:00:00 2001 From: Sphynx <327388905@qq.com> Date: Wed, 22 May 2019 22:51:16 +0800 Subject: [PATCH 371/643] add exception demo --- app/Exception/ApiException.php | 19 ++++++++ app/Exception/Handler/ApiExceptionHandler.php | 44 +++++++++++++++++++ app/Http/Controller/ExceptionController.php | 30 +++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 app/Exception/ApiException.php create mode 100644 app/Exception/Handler/ApiExceptionHandler.php create mode 100644 app/Http/Controller/ExceptionController.php diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php new file mode 100644 index 00000000..fe7612ac --- /dev/null +++ b/app/Exception/ApiException.php @@ -0,0 +1,19 @@ + $e->getCode(), + 'error' => sprintf('(%s) %s', get_class($e), $e->getMessage()), + 'file' => sprintf('At %s line %d', $e->getFile(), $e->getLine()), + 'trace' => $e->getTraceAsString(), + ]; + return $response->withData($data); + } +} diff --git a/app/Http/Controller/ExceptionController.php b/app/Http/Controller/ExceptionController.php new file mode 100644 index 00000000..c595299a --- /dev/null +++ b/app/Http/Controller/ExceptionController.php @@ -0,0 +1,30 @@ + Date: Thu, 23 May 2019 12:20:21 +0800 Subject: [PATCH 372/643] Modify ApiExceptionHandler --- app/Exception/Handler/ApiExceptionHandler.php | 12 ++++++------ runtime/.keep | 0 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 runtime/.keep diff --git a/app/Exception/Handler/ApiExceptionHandler.php b/app/Exception/Handler/ApiExceptionHandler.php index e9c6725a..6c7c615d 100644 --- a/app/Exception/Handler/ApiExceptionHandler.php +++ b/app/Exception/Handler/ApiExceptionHandler.php @@ -1,4 +1,4 @@ - $e->getCode(), - 'error' => sprintf('(%s) %s', get_class($e), $e->getMessage()), - 'file' => sprintf('At %s line %d', $e->getFile(), $e->getLine()), - 'trace' => $e->getTraceAsString(), + 'code' => $except->getCode(), + 'error' => sprintf('(%s) %s', get_class($except), $except->getMessage()), + 'file' => sprintf('At %s line %d', $except->getFile(), $except->getLine()), + 'trace' => $except->getTraceAsString(), ]; return $response->withData($data); } diff --git a/runtime/.keep b/runtime/.keep deleted file mode 100644 index e69de29b..00000000 From f4fd33b60761bf98d96f9a237a31af04faebb0b8 Mon Sep 17 00:00:00 2001 From: Sphynx <327388905@qq.com> Date: Thu, 23 May 2019 12:28:34 +0800 Subject: [PATCH 373/643] Modify ApiExceptionHandler --- app/Exception/Handler/ApiExceptionHandler.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/app/Exception/Handler/ApiExceptionHandler.php b/app/Exception/Handler/ApiExceptionHandler.php index 6c7c615d..8ef7b51b 100644 --- a/app/Exception/Handler/ApiExceptionHandler.php +++ b/app/Exception/Handler/ApiExceptionHandler.php @@ -1,14 +1,4 @@ Date: Sun, 26 May 2019 19:45:59 +0800 Subject: [PATCH 374/643] Added request bean --- app/Http/Controller/BeanController.php | 39 ++++++++++++++++++++++++++ app/Model/Logic/RequestBean.php | 24 ++++++++++++++++ app/bean.php | 2 +- 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controller/BeanController.php create mode 100644 app/Model/Logic/RequestBean.php diff --git a/app/Http/Controller/BeanController.php b/app/Http/Controller/BeanController.php new file mode 100644 index 00000000..5dc0d584 --- /dev/null +++ b/app/Http/Controller/BeanController.php @@ -0,0 +1,39 @@ +getData(); + } +} \ No newline at end of file diff --git a/app/Model/Logic/RequestBean.php b/app/Model/Logic/RequestBean.php new file mode 100644 index 00000000..56412142 --- /dev/null +++ b/app/Model/Logic/RequestBean.php @@ -0,0 +1,24 @@ + [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.3', + 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', ], From e6f08c7de0a7f3ae71f47f470f0093cd5bf6750c Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 26 May 2019 19:50:25 +0800 Subject: [PATCH 375/643] add bean --- app/Console/Command/TestCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 852e1374..0768c23a 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -88,6 +88,9 @@ private function uris(): array ], 'co' => [ '/co/multi' + ], + 'bean' => [ + '/bean/request' ] ]; } From 4982e394046f3ba1f2c69935131f3bc0437b1f7b Mon Sep 17 00:00:00 2001 From: Sphynx <327388905@qq.com> Date: Mon, 27 May 2019 17:27:44 +0800 Subject: [PATCH 376/643] Add Exception demo --- app/Exception/ApiException.php | 6 +++++- app/Exception/Handler/ApiExceptionHandler.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php index fe7612ac..9db7c295 100644 --- a/app/Exception/ApiException.php +++ b/app/Exception/ApiException.php @@ -12,7 +12,11 @@ namespace App\Exception; - +/** + * Class ApiException + * + * @sunny 2.0 + */ class ApiException extends \Exception { diff --git a/app/Exception/Handler/ApiExceptionHandler.php b/app/Exception/Handler/ApiExceptionHandler.php index 8ef7b51b..0969c25b 100644 --- a/app/Exception/Handler/ApiExceptionHandler.php +++ b/app/Exception/Handler/ApiExceptionHandler.php @@ -20,7 +20,7 @@ class ApiExceptionHandler extends AbstractHttpErrorHandler { /** - * @param Throwable $e + * @param Throwable $except * @param Response $response * * @return Response From d2eec018e842e52d394bc252bbb2d8c10cd57769 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 28 May 2019 19:27:06 +0800 Subject: [PATCH 377/643] Formatted --- app/Exception/ApiException.php | 13 +------------ app/Exception/Handler/ApiExceptionHandler.php | 2 +- app/Http/Controller/ExceptionController.php | 13 ++----------- 3 files changed, 4 insertions(+), 24 deletions(-) diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php index 9db7c295..bae2e370 100644 --- a/app/Exception/ApiException.php +++ b/app/Exception/ApiException.php @@ -1,21 +1,10 @@ Date: Tue, 28 May 2019 21:13:18 +0800 Subject: [PATCH 378/643] Add rpc error handler --- .gitignore | 1 + app/Exception/Handler/RpcExceptionHandler.php | 51 +++++++++++++++++++ app/Http/Controller/RpcController.php | 18 ++++++- app/Rpc/Lib/UserInterface.php | 5 ++ app/Rpc/Service/UserService.php | 10 ++++ app/Rpc/Service/UserServiceV2.php | 10 ++++ app/bean.php | 2 +- 7 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 app/Exception/Handler/RpcExceptionHandler.php diff --git a/.gitignore b/.gitignore index 610d543f..efc949ab 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ temp/ *.lock .phpintel/ .env +.phpstorm.meta.php .DS_Store public/devtool/ \ No newline at end of file diff --git a/app/Exception/Handler/RpcExceptionHandler.php b/app/Exception/Handler/RpcExceptionHandler.php new file mode 100644 index 00000000..9f111254 --- /dev/null +++ b/app/Exception/Handler/RpcExceptionHandler.php @@ -0,0 +1,51 @@ +getMessage(), $e->getFile(), $e->getLine()); + $error = Error::new($e->getCode(), $message, null); + } else { + $error = Error::new($e->getCode(), $e->getMessage(), null); + } + + Debug::log('Rpc server error(%s)', $e->getMessage()); + + $response->setError($error); + + // Debug is true + return $response; + } +} \ No newline at end of file diff --git a/app/Http/Controller/RpcController.php b/app/Http/Controller/RpcController.php index 277f1e4c..c216652f 100644 --- a/app/Http/Controller/RpcController.php +++ b/app/Http/Controller/RpcController.php @@ -4,7 +4,7 @@ namespace App\Http\Controller; use App\Rpc\Lib\UserInterface; -use Swoft\Bean\Annotation\Mapping\Bean; +use Exception; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Rpc\Client\Annotation\Mapping\Reference; @@ -68,8 +68,22 @@ public function returnBool(): array */ public function bigString(): array { - $string = $this->userService->getBigContent(); + $this->userService->getBigContent(); return ['string']; } + + /** + * @RequestMapping() + * + * @return array + * + * @throws Exception + */ + public function exception(): array + { + $this->userService->exception(); + + return ['exception']; + } } \ No newline at end of file diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index 9d646d5b..4440c6cb 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -30,4 +30,9 @@ public function delete(int $id): bool; * @return string */ public function getBigContent(): string; + + /** + * Exception + */ + public function exception(): void; } \ No newline at end of file diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index 09196540..5d36df1b 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -5,6 +5,7 @@ use App\Rpc\Lib\UserInterface; +use Exception; use Swoft\Co; use Swoft\Rpc\Server\Annotation\Mapping\Service; @@ -47,4 +48,13 @@ public function getBigContent(): string $content = Co::readFile(__DIR__ . '/big.data'); return $content; } + + /** + * Exception + * @throws Exception + */ + public function exception(): void + { + throw new Exception('exception version'); + } } \ No newline at end of file diff --git a/app/Rpc/Service/UserServiceV2.php b/app/Rpc/Service/UserServiceV2.php index e42ac96c..2e59f0da 100644 --- a/app/Rpc/Service/UserServiceV2.php +++ b/app/Rpc/Service/UserServiceV2.php @@ -5,6 +5,7 @@ use App\Rpc\Lib\UserInterface; +use Exception; use Swoft\Co; use Swoft\Rpc\Server\Annotation\Mapping\Service; @@ -50,4 +51,13 @@ public function getBigContent(): string $content = Co::readFile(__DIR__ . '/big.data'); return $content; } + + /** + * Exception + * @throws Exception + */ + public function exception(): void + { + throw new Exception('exception version2'); + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index f3492abe..d089bc40 100644 --- a/app/bean.php +++ b/app/bean.php @@ -36,7 +36,7 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=127.0.0.1', + 'dsn' => 'mysql:dbname=test;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', ], From d13477b87b342d1cf82ee4bfe1801e4f0df597da Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 2 Jun 2019 19:19:57 +0800 Subject: [PATCH 379/643] Add read me --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 80adccb7..983b7c87 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,57 @@ - Flexible exception handling - Powerful log system -## Notice +## Document -> The **master** is version `2.0`. If you want to use the 1.x version, please choose the 1.x branch code. +[中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) + +[English](https://www.swoft.org/docs/2.x/zh-CN/README.html) + +QQ Group1: 548173319 +QQ Group2: 778656850 + +## Requirement + +- [PHP 7.1 +](https://github.com/php/php-src/releases) +- [Swoole 4.3.4 + ](https://github.com/swoole/swoole-src/releases) +- [Composer](https://getcomposer.org/) + +## Install + +### Composer + +* `composer create-project swoft/swoft swoft` + +## Start + +```python +[root@swoft swoft]# php bin/swoft http:start +2019/06/02-11:18:06 [INFO] Swoole\Runtime::enableCoroutine +2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @base=/data/www/swoft +2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @app=@base/app +2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @config=@base/config +2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @runtime=@base/runtime +2019/06/02-11:18:06 [INFO] Project path is /data/www/swoft +2019/06/02-11:18:06 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Env file(/data/www/swoft/.env) is loaded +2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Annotations is scanned(autoloader 23, annotation 226, parser 57) +2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) config path=/data/www/swoft/config +2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) config env= +2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Bean is initialized(singleton 144, prototype 41, definition 30) +2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Event manager initialized(30 listener, 3 subscriber) +2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) WebSocket server route registered(module 2, message command 3) +2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) Error manager init completed(2 type, 3 handler, 3 exception) +2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Console command route registered (group 14, command 5) + Information Panel + *********************************************************************** + * HTTP | Listen: 0.0.0.0:18306, type: TCP, mode: Process, worker: 1 + * rpc | Listen: 0.0.0.0:18307, type: TCP + *********************************************************************** + +HTTP server start success ! +2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) Registered swoole events: + start, shutdown, managerStart, managerStop, workerStart, workerStop, workerError, request, task, finish +Server start success (Master PID: 249, Manager PID: 250) +``` ## License From f2515af5a0c8b218652b9c65534a23a4dcaa6964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Sun, 2 Jun 2019 19:22:29 +0800 Subject: [PATCH 380/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 983b7c87..6a1578c9 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ QQ Group2: 778656850 ## Start -```python +``` [root@swoft swoft]# php bin/swoft http:start 2019/06/02-11:18:06 [INFO] Swoole\Runtime::enableCoroutine 2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @base=/data/www/swoft From 1aa81ff05808c1ef645ebd4a7791d500f62b1cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Sun, 2 Jun 2019 19:23:21 +0800 Subject: [PATCH 381/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a1578c9..e91bab6f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

      -[![Latest Version](https://img.shields.io/badge/version-v2.0.0-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Latest Version](https://img.shields.io/badge/version-v2.0.1-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) From 9a6eb452d48c0aa046239ecb91bfad1449e99d6d Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 9 Jun 2019 16:51:47 +0800 Subject: [PATCH 382/643] Add db select --- app/Console/Command/TestCommand.php | 8 +- app/Http/Controller/SelectDbController.php | 206 +++++++++++++++++++++ app/bean.php | 2 +- bin/test.php | 1 - 4 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controller/SelectDbController.php delete mode 100644 bin/test.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index d78201f6..23da683a 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -37,7 +37,7 @@ public function ab() } foreach ($exeUris as $uri) { - $abShell = sprintf('ab -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $abShell = sprintf('ab -k -n 10000 -c 2000 127.0.0.1:18306%s', $uri); $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); exec($curlShell, $curlResult); @@ -74,6 +74,12 @@ private function uris(): array '/dbModel/update', '/dbModel/delete', '/dbModel/save', + '/selectDb/modelNotExistDb', + '/selectDb/queryNotExistDb', + '/selectDb/dbNotExistDb', + '/selectDb/modelDb', + '/selectDb/queryDb', + '/selectDb/dbDb', ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/SelectDbController.php b/app/Http/Controller/SelectDbController.php new file mode 100644 index 00000000..ae8b25f0 --- /dev/null +++ b/app/Http/Controller/SelectDbController.php @@ -0,0 +1,206 @@ +getId(); + + $user = User::find($id)->toArray(); + + sgo(function () { + $id = $this->getId(); + + User::find($id)->toArray(); + + User::db("test_error"); + User::db("test_error")->find($id); + }); + + User::db("test_error"); + User::db("test_error")->find($id); + + return $user; + } + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function modelDb(): array + { + $id = $this->getId(); + $user = User::find($id)->toArray(); + + $this->insertId2(); + $result = User::db('test2')->count('id'); + + sgo(function () { + $id = $this->getId(); + User::find($id)->toArray(); + + $this->insertId2(); + User::db('test2')->count('id'); + }); + + return [$user, $result]; + } + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function queryNotExistDb(): array + { + $id = $this->getId(); + + $user = User::find($id)->toArray(); + + DB::table('user')->db('test_error'); + DB::table('user')->db('test_error')->where('id', '=', $id)->get(); + + sgo(function () { + $id = $this->getId(); + + User::find($id)->toArray(); + + DB::table('user')->db('test_error'); + DB::table('user')->db('test_error')->where('id', '=', $id)->get(); + }); + + return $user; + } + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function queryDb(): array + { + $id = $this->getId(); + + $user = User::find($id)->toArray(); + + $this->insertId2(); + + $count = DB::table('user')->db('test2')->count(); + + sgo(function () { + $id = $this->getId(); + + User::find($id)->toArray(); + + $this->insertId2(); + + DB::table('user')->db('test2')->count(); + }); + + return [$user, $count]; + } + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function dbNotExistDb(): array + { + $id = $this->getId(); + + $user = User::find($id)->toArray(); + + sgo(function () { + $id = $this->getId(); + + User::find($id)->toArray(); + + DB::db('test_error'); + }); + + DB::db('test_error'); + + return $user; + } + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function dbDb(): array + { + $id = $this->getId(); + $user = User::find($id)->toArray(); + + $result = DB::db('test2')->selectOne('select * from user limit 1'); + + sgo(function () { + $id = $this->getId(); + User::find($id)->toArray(); + + DB::db('test2')->selectOne('select * from user limit 1'); + }); + + return [$user, $result]; + } + + public function insertId2(): bool + { + $result = User::db('test2')->insert([ + [ + 'name' => uniqid(), + 'password' => md5(uniqid()), + 'age' => mt_rand(1, 100), + 'user_desc' => 'u desc', + 'foo' => 'bar' + ] + ]); + + return $result; + } + + /** + * @return int + * @throws Throwable + */ + public function getId(): int + { + $user = new User(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + + return $user->getId(); + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index d089bc40..683451ff 100644 --- a/app/bean.php +++ b/app/bean.php @@ -36,7 +36,7 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.3', + 'dsn' => 'mysql:dbname=test;host=172.17.0.4', 'username' => 'root', 'password' => 'swoft123456', ], diff --git a/bin/test.php b/bin/test.php deleted file mode 100644 index b3d9bbc7..00000000 --- a/bin/test.php +++ /dev/null @@ -1 +0,0 @@ - Date: Sun, 9 Jun 2019 18:04:00 +0800 Subject: [PATCH 383/643] Add selector test --- app/Common/DbSelector.php | 35 +++++++ app/Console/Command/TestCommand.php | 2 + app/Http/Controller/SelectDbController.php | 21 ++++ app/Model/Entity/Count.php | 113 +++++++++++++++++++++ app/bean.php | 15 ++- bin/test.php | 15 +++ 6 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 app/Common/DbSelector.php create mode 100644 app/Model/Entity/Count.php create mode 100644 bin/test.php diff --git a/app/Common/DbSelector.php b/app/Common/DbSelector.php new file mode 100644 index 00000000..0ecc7f69 --- /dev/null +++ b/app/Common/DbSelector.php @@ -0,0 +1,35 @@ +getRequest()->query('id', 0); + $createDbName = $connection->getDb(); + + if ($selectIndex == 0) { + $selectIndex = ''; + } + + $dbName = sprintf('%s%s', $createDbName, (string)$selectIndex); + $connection->db($dbName); + } +} \ No newline at end of file diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 23da683a..00f15783 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -80,6 +80,8 @@ private function uris(): array '/selectDb/modelDb', '/selectDb/queryDb', '/selectDb/dbDb', + '/selectDb/select?id=2', + '/selectDb/select', ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/SelectDbController.php b/app/Http/Controller/SelectDbController.php index ae8b25f0..c0496fd4 100644 --- a/app/Http/Controller/SelectDbController.php +++ b/app/Http/Controller/SelectDbController.php @@ -3,7 +3,9 @@ namespace App\Http\Controller; +use App\Model\Entity\Count; use App\Model\Entity\User; +use Exception; use Swoft\Db\DB; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; @@ -174,6 +176,25 @@ public function dbDb(): array return [$user, $result]; } + /** + * @RequestMapping() + * + * @return array + * @throws Exception + */ + public function select(): array + { + $count = new Count(); + $count->setUserId(mt_rand(1, 100)); + $count->setAttributes('attr'); + $count->setCreateTime(time()); + + $result = $count->save(); + + return [$result, $count->getId()]; + + } + public function insertId2(): bool { $result = User::db('test2')->insert([ diff --git a/app/Model/Entity/Count.php b/app/Model/Entity/Count.php new file mode 100644 index 00000000..76236655 --- /dev/null +++ b/app/Model/Entity/Count.php @@ -0,0 +1,113 @@ +id; + } + + /** + * @param null|int $id + */ + public function setId(?int $id): void + { + $this->id = $id; + } + + /** + * @return null|int + */ + public function getUserId(): ?int + { + return $this->userId; + } + + /** + * @param null|int $userId + */ + public function setUserId(?int $userId): void + { + $this->userId = $userId; + } + + /** + * @return null|int + */ + public function getCreateTime(): ?int + { + return $this->createTime; + } + + /** + * @param null|int $createTime + */ + public function setCreateTime(?int $createTime): void + { + $this->createTime = $createTime; + } + + /** + * @return null|string + */ + public function getAttributes(): ?string + { + return $this->attributes; + } + + /** + * @param null|string $attributes + */ + public function setAttributes(?string $attributes): void + { + $this->attributes = $attributes; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 683451ff..5f387918 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,5 +1,7 @@ 'root', 'password' => 'swoft123456', ], + 'db2' => [ + 'class' => Database::class, + 'dsn' => 'mysql:dbname=test;host=172.17.0.4', + 'username' => 'root', + 'password' => 'swoft123456', + 'dbSelector' => bean(DbSelector::class) + ], + 'db2.pool' => [ + 'class' => Pool::class, + 'database' => bean('db2') + ], 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', @@ -71,7 +84,7 @@ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), ], - 'debug' => env('SWOFT_DEBUG', 0), + 'debug' => env('SWOFT_DEBUG', 0), /* @see WebSocketServer::$setting */ 'setting' => [ 'log_file' => alias('@runtime/swoole.log'), diff --git a/bin/test.php b/bin/test.php new file mode 100644 index 00000000..abcf7fbf --- /dev/null +++ b/bin/test.php @@ -0,0 +1,15 @@ + Date: Sun, 9 Jun 2019 22:59:54 +0800 Subject: [PATCH 384/643] Modify unit --- app/Console/Command/TestCommand.php | 3 +-- app/Http/Controller/SelectDbController.php | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 00f15783..5b31b8c0 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -80,8 +80,7 @@ private function uris(): array '/selectDb/modelDb', '/selectDb/queryDb', '/selectDb/dbDb', - '/selectDb/select?id=2', - '/selectDb/select', + '/selectDb/select' ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/SelectDbController.php b/app/Http/Controller/SelectDbController.php index c0496fd4..f35cf6b5 100644 --- a/app/Http/Controller/SelectDbController.php +++ b/app/Http/Controller/SelectDbController.php @@ -192,7 +192,6 @@ public function select(): array $result = $count->save(); return [$result, $count->getId()]; - } public function insertId2(): bool From e74396cbf0292e4c1c048a45285e873c93becd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Mon, 10 Jun 2019 19:59:40 +0800 Subject: [PATCH 385/643] update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e91bab6f..a75a1777 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ [中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) -[English](https://www.swoft.org/docs/2.x/zh-CN/README.html) +[English](https://www.swoft.org/docs/2.x/en) QQ Group1: 548173319 QQ Group2: 778656850 From 0c59e4b1aa62cfc44b899658b9794c3fcb0fc758 Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 11 Jun 2019 20:15:05 +0800 Subject: [PATCH 386/643] up: add View tag example --- app/Http/Controller/HomeController.php | 16 ++++++++++++++++ app/bean.php | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 560d0cb3..5c56ef79 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -12,6 +12,7 @@ use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\View\Annotation\Mapping\View; use Swoft\View\Renderer; use Throwable; use function trigger_error; @@ -38,6 +39,21 @@ public function index(): Response ->withContent($content); } + /** + * Will render view by annotation tag View + * + * @RequestMapping("/home") + * @View("home/index") + * + * @throws Throwable + */ + public function indexByViewTag(): array + { + return [ + 'msg' => 'hello' + ]; + } + /** * @RequestMapping("/hello[/{name}]") * @param string $name diff --git a/app/bean.php b/app/bean.php index 5f387918..fb29a0a6 100644 --- a/app/bean.php +++ b/app/bean.php @@ -36,6 +36,13 @@ 'task_enable_coroutine' => true ] ], + 'httpDispatcher' => [ + // Add global http middleware + 'middlewares' => [ + // Allow use @View tag + \Swoft\View\Middleware\ViewMiddleware::class, + ], + ], 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=172.17.0.4', From aabd1ca681ae8bb1b199ad2ad97e8790a5614fee Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 11 Jun 2019 20:18:50 +0800 Subject: [PATCH 387/643] update composer.json --- app/Helper/Functions.php | 5 ++++ composer.cn.json | 60 ++++++++++++++++++++++++++++++++++++++++ composer.json | 14 +++++----- 3 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 composer.cn.json diff --git a/app/Helper/Functions.php b/app/Helper/Functions.php index 4261cdd9..464f3343 100644 --- a/app/Helper/Functions.php +++ b/app/Helper/Functions.php @@ -2,3 +2,8 @@ /** * Custom global functions */ + +function user_func(): string +{ + return 'hello'; +} diff --git a/composer.cn.json b/composer.cn.json new file mode 100644 index 00000000..1b6f3bb3 --- /dev/null +++ b/composer.cn.json @@ -0,0 +1,60 @@ +{ + "name": "swoft/swoft", + "type": "project", + "keywords": [ + "php", + "swoole", + "swoft" + ], + "description": "Modern High performance AOP and Coroutine PHP Framework", + "license": "Apache-2.0", + "require": { + "php": ">7.1", + "ext-pdo": "*", + "ext-redis": "*", + "ext-json": "*", + "ext-mbstring": "*", + "swoft/db": "~2.0.0", + "swoft/i18n": "~2.0.0", + "swoft/view": "~2.0.0", + "swoft/task": "~2.0.0", + "swoft/redis": "~2.0.0", + "swoft/framework": "~2.0.0", + "swoft/http-server": "~2.0.0", + "swoft/rpc-client": "~2.0.0", + "swoft/rpc-server": "~2.0.0", + "swoft/websocket-server": "~2.0.0" + }, + "require-dev": { + "swoft/swoole-ide-helper": "dev-master", + "phpunit/phpunit": "^7.5", + "swoft/devtool": "~2.0.0" + }, + "autoload": { + "psr-4": { + "App\\": "app/" + }, + "files": [ + "app/Helper/Functions.php" + ] + }, + "autoload-dev": { + "psr-4": { + "AppTest\\Testing\\": "test/testing", + "AppTest\\Unit\\": "test/unit" + } + }, + "scripts": { + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "test": "./vendor/bin/phpunit -c phpunit.xml", + "cs-fix": "./vendor/bin/php-cs-fixer fix $1" + }, + "repositories": { + "packagist": { + "type": "composer", + "url": "https://packagist.laravel-china.org/" + } + } +} diff --git a/composer.json b/composer.json index 6434976a..96edb8ff 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "swoole", "swoft" ], - "description": "Modern High performance AOP and Coroutine PHP Framework, base on Swoole", + "description": "Modern High performance AOP and Coroutine PHP Framework", "license": "Apache-2.0", "require": { "php": ">7.1", @@ -14,15 +14,15 @@ "ext-redis": "*", "ext-json": "*", "ext-mbstring": "*", - "swoft/view": "~2.0.0", - "swoft/framework": "~2.0.0", "swoft/db": "~2.0.0", - "swoft/http-server": "~2.0.0", "swoft/i18n": "~2.0.0", + "swoft/view": "~2.0.0", + "swoft/task": "~2.0.0", "swoft/redis": "~2.0.0", + "swoft/framework": "~2.0.0", + "swoft/http-server": "~2.0.0", "swoft/rpc-client": "~2.0.0", "swoft/rpc-server": "~2.0.0", - "swoft/task": "~2.0.0", "swoft/websocket-server": "~2.0.0" }, "require-dev": { @@ -40,8 +40,8 @@ }, "autoload-dev": { "psr-4": { - "App\\Testing\\": "test/testing", - "App\\Unit\\": "test/unit" + "AppTest\\Testing\\": "test/testing", + "AppTest\\Unit\\": "test/unit" } }, "scripts": { From 78c9c48ac168a1c0a94e6cbe02f12e68a90cb83c Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 11 Jun 2019 20:28:23 +0800 Subject: [PATCH 388/643] rename folader --- config/db.php | 4 ++-- config/{user/info.php => dev/db.php} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename config/{user/info.php => dev/db.php} (100%) diff --git a/config/db.php b/config/db.php index 35b17338..c65b05fb 100644 --- a/config/db.php +++ b/config/db.php @@ -1,4 +1,4 @@ 'http://127.0.0.0.1' -]; \ No newline at end of file + 'host' => 'http://127.0.0.1/' +]; diff --git a/config/user/info.php b/config/dev/db.php similarity index 100% rename from config/user/info.php rename to config/dev/db.php From e38ff52f77dd4ae6e850e30e0d6d483d6d57dcb8 Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 11 Jun 2019 20:31:44 +0800 Subject: [PATCH 389/643] add php_cs file --- .php_cs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .php_cs diff --git a/.php_cs b/.php_cs new file mode 100644 index 00000000..15759a97 --- /dev/null +++ b/.php_cs @@ -0,0 +1,38 @@ +setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + 'header_comment' => [ + 'commentType' => 'PHPDoc', + 'header' => $header, + 'separate' => 'none' + ], + 'array_syntax' => [ + 'syntax' => 'short' + ], + 'single_quote' => true, + 'class_attributes_separation' => true, + 'no_unused_imports' => true, + 'standardize_not_equals' => true, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->exclude('public') + ->exclude('resources') + ->exclude('config') + ->exclude('runtime') + ->exclude('vendor') + ->in(__DIR__) + ) + ->setUsingCache(false); From a09cddf4647e6052f987fe50366b6ad76f58d96d Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Fri, 14 Jun 2019 10:09:17 +0800 Subject: [PATCH 390/643] add pool set --- app/Console/Command/TestCommand.php | 3 ++- app/Http/Controller/RedisController.php | 28 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 5b31b8c0..0002657d 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -37,7 +37,7 @@ public function ab() } foreach ($exeUris as $uri) { - $abShell = sprintf('ab -k -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $abShell = sprintf('ab -k -n 10000 -k -c 2000 127.0.0.1:18306%s', $uri); $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); exec($curlShell, $curlResult); @@ -59,6 +59,7 @@ private function uris(): array '/redis/et', '/redis/ep', '/redis/release', + '/redis/poolSet', ], 'log' => [ '/log/test' diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index 9aa0b081..e4cbca46 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -5,9 +5,11 @@ use Exception; use function sgo; +use Swoft\Bean\Annotation\Mapping\Inject; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Redis\Exception\RedisException; +use Swoft\Redis\Pool; use Swoft\Redis\Redis; /** @@ -18,6 +20,30 @@ */ class RedisController { + + /** + * @Inject() + * + * @var Pool + */ + private $redis; + + /** + * @RequestMapping("poolSet") + */ + public function set(): array + { + $key = 'key'; + $value = uniqid(); + + $this->redis->set($key, $value); + + $get = $this->redis->get($key); + + return [$get, $value]; + } + + /** * @RequestMapping("str") */ @@ -98,4 +124,4 @@ public function exTransaction(): array return ['exPipeline']; } -} \ No newline at end of file +} From fc5ba091925a66d369d34d999101203f2bce2194 Mon Sep 17 00:00:00 2001 From: "minbaby.zhang" Date: Fri, 14 Jun 2019 16:52:40 +0800 Subject: [PATCH 391/643] rename languages to language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 参考: https://github.com/swoft-cloud/swoft-i18n/blob/master/src/I18n.php#L44 --- resource/{languages => language}/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename resource/{languages => language}/.keep (100%) diff --git a/resource/languages/.keep b/resource/language/.keep similarity index 100% rename from resource/languages/.keep rename to resource/language/.keep From 8868a90d9639a0399188d8c855c59c41089a344a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 16 Jun 2019 09:19:03 +0800 Subject: [PATCH 392/643] Fix demo --- app/Common/DbSelector.php | 4 ++ app/Http/Controller/DbModelController.php | 9 ++- app/Http/Controller/SelectDbController.php | 40 ++++++++++++- app/Model/Entity/Desc.php | 65 ++++++++++++++++++++++ app/bean.php | 4 +- bin/test.php | 27 ++++++--- 6 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 app/Model/Entity/Desc.php diff --git a/app/Common/DbSelector.php b/app/Common/DbSelector.php index 0ecc7f69..b71fcad0 100644 --- a/app/Common/DbSelector.php +++ b/app/Common/DbSelector.php @@ -29,6 +29,10 @@ public function select(Connection $connection): void $selectIndex = ''; } + if($createDbName == 'test2'){ + $createDbName = 'test'; + } + $dbName = sprintf('%s%s', $createDbName, (string)$selectIndex); $connection->db($dbName); } diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index ba247d39..646efa03 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -5,6 +5,7 @@ use App\Model\Entity\User; use Exception; +use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Throwable; @@ -21,16 +22,18 @@ class DbModelController /** * @RequestMapping(route="find") * - * @return array + * @param Response $response + * + * @return Response * * @throws Throwable */ - public function find(): array + public function find(Response $response): Response { $id = $this->getId(); $user = User::find($id); - return $user->toArray(); + return $response->withData($user); } /** diff --git a/app/Http/Controller/SelectDbController.php b/app/Http/Controller/SelectDbController.php index f35cf6b5..791a6b9b 100644 --- a/app/Http/Controller/SelectDbController.php +++ b/app/Http/Controller/SelectDbController.php @@ -4,9 +4,13 @@ namespace App\Http\Controller; use App\Model\Entity\Count; +use App\Model\Entity\Desc; use App\Model\Entity\User; use Exception; +use ReflectionException; +use Swoft\Bean\Exception\ContainerException; use Swoft\Db\DB; +use Swoft\Db\Exception\DbException; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Throwable; @@ -61,15 +65,18 @@ public function modelDb(): array $this->insertId2(); $result = User::db('test2')->count('id'); + $desc = $this->desc(); sgo(function () { $id = $this->getId(); User::find($id)->toArray(); $this->insertId2(); User::db('test2')->count('id'); + + $this->desc(); }); - return [$user, $result]; + return [$user, $result, $desc]; } /** @@ -115,6 +122,7 @@ public function queryDb(): array $count = DB::table('user')->db('test2')->count(); + $desc = $this->desc(); sgo(function () { $id = $this->getId(); @@ -123,9 +131,11 @@ public function queryDb(): array $this->insertId2(); DB::table('user')->db('test2')->count(); + + $this->desc(); }); - return [$user, $count]; + return [$user, $count, $desc]; } /** @@ -166,14 +176,18 @@ public function dbDb(): array $result = DB::db('test2')->selectOne('select * from user limit 1'); + $desc = $this->desc(); + sgo(function () { $id = $this->getId(); User::find($id)->toArray(); DB::db('test2')->selectOne('select * from user limit 1'); + + $this->desc(); }); - return [$user, $result]; + return [$user, $result, $desc]; } /** @@ -194,6 +208,12 @@ public function select(): array return [$result, $count->getId()]; } + /** + * @return bool + * @throws ContainerException + * @throws DbException + * @throws ReflectionException + */ public function insertId2(): bool { $result = User::db('test2')->insert([ @@ -209,6 +229,20 @@ public function insertId2(): bool return $result; } + /** + * @throws ReflectionException + * @throws ContainerException + * @throws DbException + */ + public function desc(): array + { + $desc = new Desc(); + $desc->setDesc("desc"); + $desc->save(); + + return Desc::find($desc->getId())->toArray(); + } + /** * @return int * @throws Throwable diff --git a/app/Model/Entity/Desc.php b/app/Model/Entity/Desc.php new file mode 100644 index 00000000..b9e0254d --- /dev/null +++ b/app/Model/Entity/Desc.php @@ -0,0 +1,65 @@ +id; + } + + /** + * @param int $id + */ + public function setId(int $id): void + { + $this->id = $id; + } + + /** + * @return string + */ + public function getDesc(): ?string + { + return $this->desc; + } + + /** + * @param string $desc + */ + public function setDesc(string $desc): void + { + $this->desc = $desc; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 5f387918..d627d957 100644 --- a/app/bean.php +++ b/app/bean.php @@ -38,13 +38,13 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.4', + 'dsn' => 'mysql:dbname=test;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', ], 'db2' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.4', + 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) diff --git a/bin/test.php b/bin/test.php index abcf7fbf..c0727a8e 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,15 +1,24 @@ getTraits() as $traitClass){ + var_dump($traitClass->getMethods()); +} \ No newline at end of file From 6d7f03d0a00bb68c678cd95a95d598127337e590 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 16 Jun 2019 11:25:59 +0800 Subject: [PATCH 393/643] Add demo --- app/Console/Command/TestCommand.php | 2 + .../Controller/DbTransactionController.php | 40 ++++++ app/Http/Controller/SelectDbController.php | 4 +- app/Model/Entity/Count.php | 2 +- app/Model/Entity/Count2.php | 114 +++++++++++++++ app/Model/Entity/User3.php | 134 ++++++++++++++++++ app/bean.php | 10 ++ 7 files changed, 303 insertions(+), 3 deletions(-) create mode 100644 app/Model/Entity/Count2.php create mode 100644 app/Model/Entity/User3.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 5b31b8c0..d91aefce 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -37,6 +37,7 @@ public function ab() } foreach ($exeUris as $uri) { + $curlResult = null; $abShell = sprintf('ab -k -n 10000 -c 2000 127.0.0.1:18306%s', $uri); $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); @@ -70,6 +71,7 @@ private function uris(): array '/dbTransaction/ts2', '/dbTransaction/cm2', '/dbTransaction/rl2', + '/dbTransaction/multiPool', '/dbModel/find', '/dbModel/update', '/dbModel/delete', diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 13f6b006..3140024a 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -3,10 +3,14 @@ namespace App\Http\Controller; +use App\Model\Entity\Count; +use App\Model\Entity\Count2; use App\Model\Entity\User; +use App\Model\Entity\User3; use Swoft\Db\DB; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use SwoftTest\Db\Testing\Entity\Count4; use Throwable; use function sgo; @@ -153,6 +157,42 @@ public function rl2() return json_encode($user->toArray()); } + /** + * @RequestMapping() + */ + public function multiPool() + { + DB::beginTransaction(); + + // db3.pool + $user = new User3(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + $uid3 = $user->getId(); + + + //db.pool + $uid = $this->getId(); + + $count = new Count(); + $count->setUserId(mt_rand(1, 100)); + $count->setAttributes('attr'); + $count->setCreateTime(time()); + + $count->save(); + $cid = $count->getId(); + + DB::rollBack(); + + $u3 = User3::find($uid3)->toArray(); + $u = User::find($uid); + $c = Count::find($cid); + + return [$u3, $u, $c]; + } + /** * @return int * @throws Throwable diff --git a/app/Http/Controller/SelectDbController.php b/app/Http/Controller/SelectDbController.php index 791a6b9b..92e1bb9c 100644 --- a/app/Http/Controller/SelectDbController.php +++ b/app/Http/Controller/SelectDbController.php @@ -3,7 +3,7 @@ namespace App\Http\Controller; -use App\Model\Entity\Count; +use App\Model\Entity\Count2; use App\Model\Entity\Desc; use App\Model\Entity\User; use Exception; @@ -198,7 +198,7 @@ public function dbDb(): array */ public function select(): array { - $count = new Count(); + $count = new Count2(); $count->setUserId(mt_rand(1, 100)); $count->setAttributes('attr'); $count->setCreateTime(time()); diff --git a/app/Model/Entity/Count.php b/app/Model/Entity/Count.php index 76236655..b646a514 100644 --- a/app/Model/Entity/Count.php +++ b/app/Model/Entity/Count.php @@ -13,7 +13,7 @@ * * @since 2.0 * - * @Entity(table="count", pool="db2.pool") + * @Entity(table="count") */ class Count extends Model { diff --git a/app/Model/Entity/Count2.php b/app/Model/Entity/Count2.php new file mode 100644 index 00000000..71f4ed65 --- /dev/null +++ b/app/Model/Entity/Count2.php @@ -0,0 +1,114 @@ +id; + } + + /** + * @param null|int $id + */ + public function setId(?int $id): void + { + $this->id = $id; + } + + /** + * @return null|int + */ + public function getUserId(): ?int + { + return $this->userId; + } + + /** + * @param null|int $userId + */ + public function setUserId(?int $userId): void + { + $this->userId = $userId; + } + + /** + * @return null|int + */ + public function getCreateTime(): ?int + { + return $this->createTime; + } + + /** + * @param null|int $createTime + */ + public function setCreateTime(?int $createTime): void + { + $this->createTime = $createTime; + } + + /** + * @return null|string + */ + public function getAttributes(): ?string + { + return $this->attributes; + } + + /** + * @param null|string $attributes + */ + public function setAttributes(?string $attributes): void + { + $this->attributes = $attributes; + } +} \ No newline at end of file diff --git a/app/Model/Entity/User3.php b/app/Model/Entity/User3.php new file mode 100644 index 00000000..f310c2c0 --- /dev/null +++ b/app/Model/Entity/User3.php @@ -0,0 +1,134 @@ +id; + } + + /** + * @param int|null $id + */ + public function setId(?int $id): void + { + $this->id = $id; + } + + /** + * @return int|null + */ + public function getAge(): ?int + { + return $this->age; + } + + /** + * @param int|null $age + */ + public function setAge(?int $age): void + { + $this->age = $age; + } + + /** + * @return string|null + */ + public function getName(): ?string + { + return $this->name; + } + + /** + * @param string|null $name + */ + public function setName(?string $name): void + { + $this->name = $name; + } + + /** + * @return string|null + */ + public function getPwd(): ?string + { + return $this->pwd; + } + + /** + * @param string|null $pwd + */ + public function setPwd(?string $pwd): void + { + $this->pwd = $pwd; + } + + /** + * @return string|null + */ + public function getUserDesc(): ?string + { + return $this->userDesc; + } + + /** + * @param string|null $userDesc + */ + public function setUserDesc(?string $userDesc): void + { + $this->userDesc = $userDesc; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index d627d957..f0604b1d 100644 --- a/app/bean.php +++ b/app/bean.php @@ -53,6 +53,16 @@ 'class' => Pool::class, 'database' => bean('db2') ], + 'db3' => [ + 'class' => Database::class, + 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', + 'username' => 'root', + 'password' => 'swoft123456' + ], + 'db3.pool' => [ + 'class' => Pool::class, + 'database' => bean('db3') + ], 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', From 1af8dcd27be38477567101e96d0c88c9c0a9e4d5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 16 Jun 2019 11:27:23 +0800 Subject: [PATCH 394/643] Add demo --- app/Http/Controller/DbTransactionController.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 3140024a..048689a9 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -4,13 +4,11 @@ namespace App\Http\Controller; use App\Model\Entity\Count; -use App\Model\Entity\Count2; use App\Model\Entity\User; use App\Model\Entity\User3; use Swoft\Db\DB; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; -use SwoftTest\Db\Testing\Entity\Count4; use Throwable; use function sgo; From f9d7a5e8fa76564e98ba2e56528c62f020cfc59b Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 19 Jun 2019 21:08:51 +0800 Subject: [PATCH 395/643] Create Dockerfile --- Dockerfile | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..679f568f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,62 @@ +FROM php:7.1 + +LABEL maintainer="inhere " version="2.0" + +# Version +ENV PHPREDIS_VERSION=4.3.0 \ + SWOOLE_VERSION 4.3.5 + +ADD . /var/www/swoft + +# Timezone +RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo 'Asia/Shanghai' > /etc/timezone \ +# Libs + && apt-get update \ + && apt-get install -y \ + curl \ + wget \ + git \ + zip \ + libz-dev \ + libssl-dev \ + libnghttp2-dev \ + libpcre3-dev \ + && apt-get clean \ + && apt-get autoremove \ +# Composer + && curl -sS https://getcomposer.org/installer | php \ + && mv composer.phar /usr/local/bin/composer \ + && composer self-update --clean-backups \ +# PDO extension + && docker-php-ext-install pdo_mysql \ +# Bcmath extension + && docker-php-ext-install bcmath \ +# Redis extension + && wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \ + && pecl install /tmp/redis.tar.tgz \ + && rm -rf /tmp/redis.tar.tgz \ + && docker-php-ext-enable redis \ +# Swoole extension + && wget https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz -O swoole.tar.gz \ + && mkdir -p swoole \ + && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ + && rm swoole.tar.gz \ + && ( \ + cd swoole \ + && phpize \ + && ./configure --enable-mysqlnd --enable-sockets --enable-openssl --enable-http2 \ + && make -j$(nproc) \ + && make install \ + ) \ + && rm -r swoole \ + && docker-php-ext-enable swoole \ +# Install composer deps + && composer install --no-dev \ + && composer dump-autoload -o \ + && composer clearcache + +WORKDIR /var/www/swoft +EXPOSE 18306 18307 18308 + +ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "http:start"] From 645fd5aaf0acb2e28adadb4be40bf90d37e73b4f Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 19 Jun 2019 23:02:15 +0800 Subject: [PATCH 396/643] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 679f568f..a15bf2e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM php:7.1 -LABEL maintainer="inhere " version="2.0" +LABEL maintainer="inhere " version="2.0" # Version ENV PHPREDIS_VERSION=4.3.0 \ From e4e24771d103b8729b060c7d021dea47223a0fa6 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 19 Jun 2019 23:03:36 +0800 Subject: [PATCH 397/643] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a15bf2e8..74c727b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ LABEL maintainer="inhere " version="2.0" # Version ENV PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION 4.3.5 + SWOOLE_VERSION=4.3.5 ADD . /var/www/swoft From f59c4f6a6d9cc0ff1d8203339851741073160624 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 20 Jun 2019 12:02:33 +0800 Subject: [PATCH 398/643] Update Dockerfile --- Dockerfile | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 74c727b7..5f6d92e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,14 +24,18 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ libpcre3-dev \ && apt-get clean \ && apt-get autoremove \ -# Composer +# Install composer && curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ && composer self-update --clean-backups \ -# PDO extension +# Some php extension && docker-php-ext-install pdo_mysql \ -# Bcmath extension - && docker-php-ext-install bcmath \ + bcmath \ + sockets \ + zip \ + sysvmsg \ + sysvsem \ + sysvshm \ # Redis extension && wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \ && pecl install /tmp/redis.tar.tgz \ From f5c40564b53251e623f2b8f0cfd7ed90fc2329b0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 20 Jun 2019 12:34:01 +0800 Subject: [PATCH 399/643] fix docker build error --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5f6d92e7..d37c3423 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,8 +56,8 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && rm -r swoole \ && docker-php-ext-enable swoole \ # Install composer deps - && composer install --no-dev \ - && composer dump-autoload -o \ + && cd /var/www/swoft \ + && composer install \ && composer clearcache WORKDIR /var/www/swoft From 8c7161dd34d043e3eea052f587a0036ee010291e Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 20 Jun 2019 15:48:54 +0800 Subject: [PATCH 400/643] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a75a1777..64077824 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Latest Version](https://img.shields.io/badge/version-v2.0.1-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) +[![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) @@ -36,9 +37,8 @@ ## Document -[中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) - -[English](https://www.swoft.org/docs/2.x/en) +- [中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [English](https://www.swoft.org/docs/2.x/en) QQ Group1: 548173319 QQ Group2: 778656850 @@ -57,7 +57,7 @@ QQ Group2: 778656850 ## Start -``` +```text [root@swoft swoft]# php bin/swoft http:start 2019/06/02-11:18:06 [INFO] Swoole\Runtime::enableCoroutine 2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @base=/data/www/swoft From 17234899e4ead23590e8a518f8335932f6b2811d Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 22 Jun 2019 15:45:17 +0800 Subject: [PATCH 401/643] update dockerfile and add docker-compose --- Dockerfile | 61 +++++++++++++++++++++++++++++----------------- docker-compose.yml | 40 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index d37c3423..5362bfe4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,30 @@ -FROM php:7.1 +# @description php image base on the debian 9.x +# +# Some Information +# ------------------------------------------------------------------------------------ +# @link https://hub.docker.com/_/debian/ alpine image +# @link https://hub.docker.com/_/php/ php image +# @link https://github.com/docker-library/php php dockerfiles +# @see https://github.com/docker-library/php/tree/master/7.2/stretch/cli/Dockerfile +# ------------------------------------------------------------------------------------ +# @build-example docker build . -f Dockerfile -t swoft/swoft +# +FROM php:7.2 LABEL maintainer="inhere " version="2.0" -# Version -ENV PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.3.5 +# --build-arg timezone=Asia/Shanghai +ARG timezone +# app env: prod pre test dev +ARG app_env=prod +# default use www-data user +ARG work_user=www-data + +ENV APP_ENV=${app_env:-"prod"} \ + TIMEZONE=${timezone:-"Asia/Shanghai"} \ + PHPREDIS_VERSION=4.3.0 \ + SWOOLE_VERSION=4.3.5 \ + COMPOSER_ALLOW_SUPERUSER=1 ADD . /var/www/swoft @@ -13,35 +33,25 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone \ # Libs && apt-get update \ - && apt-get install -y \ - curl \ - wget \ - git \ - zip \ + && apt-get install -y --no-install-recommends \ + curl wget git zip unzip less vim openssl \ libz-dev \ libssl-dev \ libnghttp2-dev \ libpcre3-dev \ - && apt-get clean \ - && apt-get autoremove \ # Install composer && curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ && composer self-update --clean-backups \ -# Some php extension - && docker-php-ext-install pdo_mysql \ - bcmath \ - sockets \ - zip \ - sysvmsg \ - sysvsem \ - sysvshm \ -# Redis extension +# Install PHP extensions + && docker-php-ext-install \ + bcmath gd pdo_mysql mbstring sockets zip sysvmsg sysvsem sysvshm \ +# Install redis extension && wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \ && pecl install /tmp/redis.tar.tgz \ && rm -rf /tmp/redis.tar.tgz \ && docker-php-ext-enable redis \ -# Swoole extension +# Install swoole extension && wget https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz -O swoole.tar.gz \ && mkdir -p swoole \ && tar -xf swoole.tar.gz -C swoole --strip-components=1 \ @@ -55,9 +65,16 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ ) \ && rm -r swoole \ && docker-php-ext-enable swoole \ +# Clear dev deps + && apt-get clean \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ +# Timezone + && cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ + && echo "${TIMEZONE}" > /etc/timezone \ + && echo "[Date]\ndate.timezone=${TIMEZONE}" > /usr/local/etc/php/conf.d/timezone.ini \ # Install composer deps && cd /var/www/swoft \ - && composer install \ + && composer install --no-dev \ && composer clearcache WORKDIR /var/www/swoft diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..5f216e6f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3.4' +services: + swoft: + image: swoft/swoft + container_name: swoft-srv + environment: + - APP_ENV=dev + - TIMEZONE=Asia/Shanghai + restart: always + depends_on: + - mysql + - redis + ports: + - "18306:18306" + - "18307:18307" + - "18308:18308" + volumes: + - ./:/var/www + # - ./tmp/ng-conf:/etc/nginx + # - ./tmp/logs:/var/log + + mysql: + image: mysql + container_name: mysql-srv + environment: + - MYSQL_ROOT_PASSWORD=123456 + ports: + - "13306:3306" + volumes: + - ./tmp/data/mysql:/var/lib/mysql + restart: always + + redis: + container_name: redis-srv + image: redis:4-alpine + ports: + - "16379:6379" + sysctls: + net.core.somaxconn: 65535 + restart: always From 088897522498376a24ea6f9d49052b8d13d97d94 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 24 Jun 2019 15:38:35 +0800 Subject: [PATCH 402/643] Add rpc demo --- app/Console/Command/TestCommand.php | 7 ++++--- app/Http/Controller/RpcController.php | 19 +++++++++++++++++-- app/Rpc/Lib/UserInterface.php | 7 +++++++ app/Rpc/Service/UserService.php | 10 ++++++++++ app/Rpc/Service/UserServiceV2.php | 10 ++++++++++ bin/test.php | 26 +++++++------------------- 6 files changed, 55 insertions(+), 24 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 81a992ca..c3cb2f5b 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -38,8 +38,8 @@ public function ab() foreach ($exeUris as $uri) { $curlResult = null; - $abShell = sprintf('ab -k -n 10000 -c 2000 127.0.0.1:18306%s', $uri); - $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); + $abShell = sprintf('ab -k -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); exec($curlShell, $curlResult); output()->writeln('执行结果:' . json_encode($curlResult)); @@ -95,11 +95,12 @@ private function uris(): array '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', + '/rpc/sendBigString' ], 'co' => [ '/co/multi' ], - 'bean' => [ + 'bean' => [ '/bean/request' ] ]; diff --git a/app/Http/Controller/RpcController.php b/app/Http/Controller/RpcController.php index c216652f..4c5546f5 100644 --- a/app/Http/Controller/RpcController.php +++ b/app/Http/Controller/RpcController.php @@ -5,6 +5,7 @@ use App\Rpc\Lib\UserInterface; use Exception; +use Swoft\Co; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Rpc\Client\Annotation\Mapping\Reference; @@ -68,9 +69,23 @@ public function returnBool(): array */ public function bigString(): array { - $this->userService->getBigContent(); + $string = $this->userService->getBigContent(); - return ['string']; + return ['string', strlen($string)]; + } + + /** + * @RequestMapping() + * + * @return array + */ + public function sendBigString(): array + { + $content = Co::readFile(__DIR__ . '/../../Rpc/Service/big.data'); + + $len = strlen($content); + $result = $this->userService->sendBigContent($content); + return [$len, $result]; } /** diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index 4440c6cb..0b105ba2 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -35,4 +35,11 @@ public function getBigContent(): string; * Exception */ public function exception(): void; + + /** + * @param string $content + * + * @return int + */ + public function sendBigContent(string $content): int; } \ No newline at end of file diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index 5d36df1b..837dc3a2 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -57,4 +57,14 @@ public function exception(): void { throw new Exception('exception version'); } + + /** + * @param string $content + * + * @return int + */ + public function sendBigContent(string $content): int + { + return strlen($content); + } } \ No newline at end of file diff --git a/app/Rpc/Service/UserServiceV2.php b/app/Rpc/Service/UserServiceV2.php index 2e59f0da..0e318e68 100644 --- a/app/Rpc/Service/UserServiceV2.php +++ b/app/Rpc/Service/UserServiceV2.php @@ -60,4 +60,14 @@ public function exception(): void { throw new Exception('exception version2'); } + + /** + * @param string $content + * + * @return int + */ + public function sendBigContent(string $content): int + { + return strlen($content); + } } \ No newline at end of file diff --git a/bin/test.php b/bin/test.php index c0727a8e..5ecb520c 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,24 +1,12 @@ '1', + 'b' => '2' +]; - } -} +echo '----'.current($a); -class A -{ - use Tr; +var_dump(key($a)); - public function method() - { - - } -} - -$a = new ReflectionClass(A::class); -foreach ($a->getTraits() as $traitClass){ - var_dump($traitClass->getMethods()); -} \ No newline at end of file +var_dump($a); \ No newline at end of file From 65fd02152780e2006769233e792b16a08d6d84fe Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 24 Jun 2019 20:34:52 +0800 Subject: [PATCH 403/643] update: remove the laravel-china source --- app/Console/Command/DemoCommand.php | 10 +++++ app/Console/Command/TestCommand.php | 33 +++++++++++++++ app/Http/Controller/ExceptionController.php | 37 +++++++++++++++- app/Http/Controller/HomeController.php | 47 ++------------------- app/Http/Controller/RespController.php | 32 ++++++++++++++ app/Http/Controller/ViewController.php | 23 ++++++++-- 6 files changed, 133 insertions(+), 49 deletions(-) create mode 100644 app/Http/Controller/RespController.php diff --git a/app/Console/Command/DemoCommand.php b/app/Console/Command/DemoCommand.php index 7cdd930d..683c505c 100644 --- a/app/Console/Command/DemoCommand.php +++ b/app/Console/Command/DemoCommand.php @@ -4,6 +4,7 @@ use Swoft\Console\Annotation\Mapping\Command; use Swoft\Console\Annotation\Mapping\CommandMapping; +use Swoft\Console\Exception\ConsoleErrorException; use Swoft\Console\Helper\Show; use Swoft\Console\Input\Input; @@ -25,4 +26,13 @@ public function test(Input $input): void 'opts' => $input->getOptions(), ]); } + + /** + * @CommandMapping("err") + * @throws ConsoleErrorException + */ + public function coError(): void + { + ConsoleErrorException::throw('this is an error message'); + } } diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 81a992ca..9bc8fa05 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -8,6 +8,9 @@ use function input; use function output; use function sprintf; +use Swoft\Console\Exception\ConsoleErrorException; +use Swoft\Console\Helper\Show; +use Swoft\Http\Server\Router\Route; /** * Class TestCommand @@ -104,4 +107,34 @@ private function uris(): array ] ]; } + + /** + * Mock request some api for test server + * + * @CommandMapping("ca") + */ + public function checkAccess(): void + { + \bean('httpRouter')->each(function (Route $route) { + $path = $route->getPath(); + + // Skip some routes + if ($route->getMethod() !== 'GET' || false !== \strpos($path, '{')) { + return; + } + + $command = sprintf('curl -I 127.0.0.1:18306%s', $path); + Show::colored('> ' . $command); + \exec($command); + }); + } + + /** + * @CommandMapping("err") + * @throws ConsoleErrorException + */ + public function error(): void + { + ConsoleErrorException::throw('this is an error message'); + } } diff --git a/app/Http/Controller/ExceptionController.php b/app/Http/Controller/ExceptionController.php index 34e7e054..0fe8f281 100644 --- a/app/Http/Controller/ExceptionController.php +++ b/app/Http/Controller/ExceptionController.php @@ -2,8 +2,13 @@ namespace App\Http\Controller; use App\Exception\ApiException; +use RuntimeException; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Throwable; +use function trigger_error; +use const E_USER_ERROR; +use const E_USER_NOTICE; /** * @Controller(prefix="ex") @@ -15,7 +20,35 @@ class ExceptionController * * @throws ApiException */ - public function api(){ - throw new ApiException("api of ExceptionController"); + public function api(): void + { + throw new ApiException('api of ExceptionController'); + } + + /** + * @RequestMapping("/ex") + * @throws Throwable + */ + public function ex(): void + { + throw new RuntimeException('exception throw on ' . __METHOD__); + } + + /** + * @RequestMapping("/er") + * @throws Throwable + */ + public function error(): void + { + trigger_error('user error', E_USER_ERROR); + } + + /** + * @RequestMapping("/nt") + * @throws Throwable + */ + public function notice(): void + { + trigger_error('user error', E_USER_NOTICE); } } diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 5c56ef79..10aa65bd 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -2,9 +2,7 @@ namespace App\Http\Controller; -use const E_USER_ERROR; use ReflectionException; -use RuntimeException; use Swoft; use Swoft\Bean\Exception\ContainerException; use Swoft\Context\Context; @@ -12,10 +10,8 @@ use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; -use Swoft\View\Annotation\Mapping\View; use Swoft\View\Renderer; use Throwable; -use function trigger_error; /** * Class HomeController @@ -33,56 +29,19 @@ public function index(): Response $renderer = Swoft::getBean('view'); $content = $renderer->render('home/index'); - return Context::mustGet() - ->getResponse() - ->withContentType(ContentType::HTML) - ->withContent($content); - } - - /** - * Will render view by annotation tag View - * - * @RequestMapping("/home") - * @View("home/index") - * - * @throws Throwable - */ - public function indexByViewTag(): array - { - return [ - 'msg' => 'hello' - ]; + return Context::mustGet()->getResponse()->withContentType(ContentType::HTML)->withContent($content); } /** * @RequestMapping("/hello[/{name}]") * @param string $name + * * @return Response * @throws ReflectionException * @throws ContainerException */ public function hello(string $name): Response { - return Context::mustGet() - ->getResponse() - ->withContent('Hello' . ($name === '' ? '' : ", {$name}")); - } - - /** - * @RequestMapping("/ex") - * @throws Throwable - */ - public function ex(): void - { - throw new RuntimeException('exception throw on ' . __METHOD__); - } - - /** - * @RequestMapping("/er") - * @throws Throwable - */ - public function er(): void - { - trigger_error('user error', E_USER_ERROR); + return Context::mustGet()->getResponse()->withContent('Hello' . ($name === '' ? '' : ", {$name}")); } } diff --git a/app/Http/Controller/RespController.php b/app/Http/Controller/RespController.php new file mode 100644 index 00000000..5527b754 --- /dev/null +++ b/app/Http/Controller/RespController.php @@ -0,0 +1,32 @@ +getResponse(); + + return $resp->setCookie('c-name', 'c-value')->withData(['hello']); + } +} diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php index 213b25ea..56797e4c 100644 --- a/app/Http/Controller/ViewController.php +++ b/app/Http/Controller/ViewController.php @@ -9,6 +9,8 @@ use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\View\Annotation\Mapping\View; +use Throwable; /** * Class ViewController @@ -28,13 +30,28 @@ class ViewController * @throws ReflectionException * @throws ContainerException */ - public function index(Response $response) + public function index(Response $response): Response { - $response = $response->withContent('

      Swoft framework

      '); + $response = $response->withContent('

      Swoft framework

      '); $response = $response->withContentType(ContentType::HTML); return $response; } + /** + * Will render view by annotation tag View + * + * @RequestMapping("/home") + * @View("home/index") + * + * @throws Throwable + */ + public function indexByViewTag(): array + { + return [ + 'msg' => 'hello' + ]; + } + /** * @RequestMapping() * @@ -54,4 +71,4 @@ public function str(): string { return 'string'; } -} \ No newline at end of file +} From 3a0f75397ab3d7422c207170b4d8271eff792660 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 25 Jun 2019 11:06:35 +0800 Subject: [PATCH 404/643] Fixed dev.composer.json --- bin/test.php | 86 +++++++++++++++++++++++++++++++++++++++++++---- dev.composer.json | 47 +++++--------------------- 2 files changed, 87 insertions(+), 46 deletions(-) diff --git a/bin/test.php b/bin/test.php index 5ecb520c..da21b4ce 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,12 +1,84 @@ '1', - 'b' => '2' -]; +use Swoft\Bean\Annotation\Mapping\Bean; +use Swoft\Bean\Annotation\Mapping\Inject; +use Swoft\Bean\Annotation\Mapping\Primary; +use Swoft\Bean\BeanFactory; -echo '----'.current($a); +interface SmsInterface +{ + public function send(string $content): bool; +} -var_dump(key($a)); -var_dump($a); \ No newline at end of file +/** + * Class AliyunSms + * + * @since 2.0 + * + * @Bean() + * @Primary() + */ +class AliyunSms implements SmsInterface +{ + /** + * @param string $content + * + * @return bool + */ + public function send(string $content): bool + { + return true; + } +} + +/** + * Class QcloudSms + * + * @since 2.0 + * + * @Bean() + */ +class QcloudSms implements SmsInterface +{ + /** + * @param string $content + * + * @return bool + */ + public function send(string $content): bool + { + return true; + } +} + +/** + * Class Sms + * + * @since 2.0 + * + * @Bean() + */ +class Sms implements SmsInterface +{ + /** + * @Inject() + * + * @var SmsInterface + */ + private $smsInterface; + + /** + * @param string $content + * + * @return bool + */ + public function send(string $content): bool + { + return $this->smsInterface->send($content); + } +} + +/* @var SmsInterface $sms*/ +$sms = BeanFactory::getBean(Sms::class); +$sms->send('sms content'); \ No newline at end of file diff --git a/dev.composer.json b/dev.composer.json index a9aea4c9..a90aaded 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -13,32 +13,9 @@ "ext-pdo": "*", "ext-json": "*", "ext-swoole": ">=4.3", - "swoft/annotation": "dev-master", - "swoft/bean": "dev-master", - "swoft/event": "dev-master", - "swoft/aop": "dev-master", - "swoft/config": "dev-master", - "swoft/stdlib": "dev-master", - "swoft/framework": "dev-master", - "swoft/http-message": "dev-master", - "swoft/server": "dev-master", - "swoft/tcp-server": "dev-master", - "swoft/http-server": "dev-master", - "swoft/websocket-server": "dev-master", - "swoft/log": "dev-master", - "swoft/db": "dev-master", - "swoft/connection-pool": "dev-master", - "swoft/test": "dev-master", - "swoft/console": "dev-master", - "swoft/rpc": "dev-master", - "swoft/rpc-server": "dev-master", - "swoft/rpc-client": "dev-master", - "swoft/task": "dev-master", - "swoft/redis": "dev-master", - "swoft/proxy": "dev-master", - "swoft/error": "dev-master", - "swoft/view": "dev-master", - "swoft/devtool": "dev-master" + "swoft/component": "dev-master as 2.0", + "swoft/view": "~2.0.0", + "swoft/devtool": "~2.0.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", @@ -65,18 +42,10 @@ "test": "./vendor/bin/phpunit -c phpunit.xml", "cs-fix": "./vendor/bin/php-cs-fixer fix $1" }, - "repositories": { - "packagist": { - "type": "composer", - "url": "https://packagist.laravel-china.org/" - }, - "swoft": { - "type": "path", - "url": "../swoft-component/src/*" - }, - "swoft/devtool": { - "type": "path", - "url": "../swoft-devtool" + "repositories": [ + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-component.git" } - } + ] } From b8fd1c6abf445600eb31e371d5dabd54ccb2ca0b Mon Sep 17 00:00:00 2001 From: JasonYHZ Date: Tue, 25 Jun 2019 17:33:18 +0800 Subject: [PATCH 405/643] add Custom validation rules demo add Custom annotation demo add Custom validator demo add ValidatorController demo --- app/Annotation/Mapping/AlphaDash.php | 64 +++++++++++++++++++ app/Annotation/Parser/AlphaDashParser.php | 35 +++++++++++ app/Http/Controller/ValidatorController.php | 69 +++++++++++++++++++++ app/Validator/CustomerValidator.php | 37 +++++++++++ app/Validator/Rule/AlphaDashRule.php | 39 ++++++++++++ app/Validator/TestValidator.php | 39 ++++++++++++ 6 files changed, 283 insertions(+) create mode 100644 app/Annotation/Mapping/AlphaDash.php create mode 100644 app/Annotation/Parser/AlphaDashParser.php create mode 100644 app/Http/Controller/ValidatorController.php create mode 100644 app/Validator/CustomerValidator.php create mode 100644 app/Validator/Rule/AlphaDashRule.php create mode 100644 app/Validator/TestValidator.php diff --git a/app/Annotation/Mapping/AlphaDash.php b/app/Annotation/Mapping/AlphaDash.php new file mode 100644 index 00000000..a5ccdb29 --- /dev/null +++ b/app/Annotation/Mapping/AlphaDash.php @@ -0,0 +1,64 @@ +message = $values['value']; + } + if (isset($values['message'])) { + $this->message = $values['message']; + } + if (isset($values['name'])) { + $this->name = $values['name']; + } + } + + /** + * @return string + */ + public function getMessage(): string + { + return $this->message; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } +} diff --git a/app/Annotation/Parser/AlphaDashParser.php b/app/Annotation/Parser/AlphaDashParser.php new file mode 100644 index 00000000..23c66ac5 --- /dev/null +++ b/app/Annotation/Parser/AlphaDashParser.php @@ -0,0 +1,35 @@ +className, $this->propertyName, $annotationObject); + return []; + } +} diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php new file mode 100644 index 00000000..84834d02 --- /dev/null +++ b/app/Http/Controller/ValidatorController.php @@ -0,0 +1,69 @@ +getParsedBody(); + } + + /** + * Verify only the type field in the TestValidator validator + * @RequestMapping() + * @Validate(validator="TestValidator",fields={"type"}) + * @param Request $request + * + * @return array + */ + function validateType(Request $request): array + { + return $request->getParsedBody(); + } + + /** + * Verify only the password field in the TestValidator validator + * @RequestMapping() + * @Validate(validator="TestValidator",fields={"password"}) + * @param Request $request + * + * @return array + */ + function validatePassword(Request $request): array + { + return $request->getParsedBody(); + } + + /** + * Customize the validator with userValidator + * @RequestMapping() + * @Validate(validator="userValidator") + * @param Request $request + * + * @return array + */ + function validateCustomer(Request $request): array + { + return $request->getParsedBody(); + + } +} diff --git a/app/Validator/CustomerValidator.php b/app/Validator/CustomerValidator.php new file mode 100644 index 00000000..a832c752 --- /dev/null +++ b/app/Validator/CustomerValidator.php @@ -0,0 +1,37 @@ + $end) { + throw new ValidatorException('Start cannot be greater than the end time'); + } + return $data; + } +} diff --git a/app/Validator/Rule/AlphaDashRule.php b/app/Validator/Rule/AlphaDashRule.php new file mode 100644 index 00000000..05f2395c --- /dev/null +++ b/app/Validator/Rule/AlphaDashRule.php @@ -0,0 +1,39 @@ +getMessage(); + if (!isset($data[$propertyName]) && $default === null) { + $message = (empty($message)) ? sprintf('%s must exist!', $propertyName) : $message; + throw new ValidatorException($message); + } + $rule = '/^[A-Za-z0-9\-\_]+$/'; + if (preg_match($rule, $data[$propertyName])) { + return [$data]; + } + $message = (empty($message)) ? sprintf('%s must be a email', $propertyName) : $message; + throw new ValidatorException($message); + } +} diff --git a/app/Validator/TestValidator.php b/app/Validator/TestValidator.php new file mode 100644 index 00000000..57a9f3ea --- /dev/null +++ b/app/Validator/TestValidator.php @@ -0,0 +1,39 @@ + Date: Tue, 25 Jun 2019 17:43:26 +0800 Subject: [PATCH 406/643] format code --- app/Http/Controller/ValidatorController.php | 4 ++++ app/Validator/Rule/AlphaDashRule.php | 1 + app/Validator/TestValidator.php | 1 + 3 files changed, 6 insertions(+) diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php index 84834d02..c06a65aa 100644 --- a/app/Http/Controller/ValidatorController.php +++ b/app/Http/Controller/ValidatorController.php @@ -16,6 +16,7 @@ class ValidatorController { /** * Verify all defined fields in the TestValidator validator + * * @RequestMapping() * @Validate(validator="TestValidator") * @param Request $request @@ -29,6 +30,7 @@ function validateAll(Request $request): array /** * Verify only the type field in the TestValidator validator + * * @RequestMapping() * @Validate(validator="TestValidator",fields={"type"}) * @param Request $request @@ -42,6 +44,7 @@ function validateType(Request $request): array /** * Verify only the password field in the TestValidator validator + * * @RequestMapping() * @Validate(validator="TestValidator",fields={"password"}) * @param Request $request @@ -55,6 +58,7 @@ function validatePassword(Request $request): array /** * Customize the validator with userValidator + * * @RequestMapping() * @Validate(validator="userValidator") * @param Request $request diff --git a/app/Validator/Rule/AlphaDashRule.php b/app/Validator/Rule/AlphaDashRule.php index 05f2395c..8180b12d 100644 --- a/app/Validator/Rule/AlphaDashRule.php +++ b/app/Validator/Rule/AlphaDashRule.php @@ -19,6 +19,7 @@ class AlphaDashRule implements RuleInterface * @param string $propertyName * @param object $item * @param null $default + * * @return array * @throws ValidatorException */ diff --git a/app/Validator/TestValidator.php b/app/Validator/TestValidator.php index 57a9f3ea..84652ef6 100644 --- a/app/Validator/TestValidator.php +++ b/app/Validator/TestValidator.php @@ -32,6 +32,7 @@ class TestValidator /** * @AlphaDash(message="Passwords can only be alphabet, numbers, dashes, underscores") + * * @var string */ protected $password; From 2d734090646ef0ea5257e09f1c5be4c9ab7ce1cc Mon Sep 17 00:00:00 2001 From: JasonYHZ Date: Tue, 25 Jun 2019 22:47:35 +0800 Subject: [PATCH 407/643] Remove the inheritance Type class of AlphaDash Formatting code style --- app/Annotation/Mapping/AlphaDash.php | 3 +-- app/Http/Controller/ValidatorController.php | 5 ++++- app/Validator/TestValidator.php | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/Annotation/Mapping/AlphaDash.php b/app/Annotation/Mapping/AlphaDash.php index a5ccdb29..67bb235b 100644 --- a/app/Annotation/Mapping/AlphaDash.php +++ b/app/Annotation/Mapping/AlphaDash.php @@ -4,7 +4,6 @@ use Doctrine\Common\Annotations\Annotation\Attribute; use Doctrine\Common\Annotations\Annotation\Attributes; -use Swoft\Validator\Annotation\Mapping\Type; /** * Class AlphaDash @@ -16,7 +15,7 @@ * @Attribute("message",type="string") * }) */ -class AlphaDash extends Type +class AlphaDash { /** * @var string diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php index c06a65aa..8f339209 100644 --- a/app/Http/Controller/ValidatorController.php +++ b/app/Http/Controller/ValidatorController.php @@ -19,6 +19,7 @@ class ValidatorController * * @RequestMapping() * @Validate(validator="TestValidator") + * * @param Request $request * * @return array @@ -33,6 +34,7 @@ function validateAll(Request $request): array * * @RequestMapping() * @Validate(validator="TestValidator",fields={"type"}) + * * @param Request $request * * @return array @@ -47,6 +49,7 @@ function validateType(Request $request): array * * @RequestMapping() * @Validate(validator="TestValidator",fields={"password"}) + * * @param Request $request * * @return array @@ -61,6 +64,7 @@ function validatePassword(Request $request): array * * @RequestMapping() * @Validate(validator="userValidator") + * * @param Request $request * * @return array @@ -68,6 +72,5 @@ function validatePassword(Request $request): array function validateCustomer(Request $request): array { return $request->getParsedBody(); - } } diff --git a/app/Validator/TestValidator.php b/app/Validator/TestValidator.php index 84652ef6..6113bfda 100644 --- a/app/Validator/TestValidator.php +++ b/app/Validator/TestValidator.php @@ -36,5 +36,4 @@ class TestValidator * @var string */ protected $password; - } From 6741df977f7fd5a0258f38a1d857658f1a679353 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 26 Jun 2019 17:53:01 +0800 Subject: [PATCH 408/643] Modify composer --- dev.composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dev.composer.json b/dev.composer.json index a90aaded..758b4856 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -14,6 +14,7 @@ "ext-json": "*", "ext-swoole": ">=4.3", "swoft/component": "dev-master as 2.0", + "swoft/ext": "dev-master as 2.0", "swoft/view": "~2.0.0", "swoft/devtool": "~2.0.0" }, @@ -46,6 +47,10 @@ { "type": "git", "url": "git@github.com:swoft-cloud/swoft-component.git" + }, + { + "type": "git", + "url": "git@github.com:swoft-cloud/swoft-ext.git" } ] } From 4d230f13dbfba0906660d2167c7db127684334a6 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 28 Jun 2019 10:33:45 +0800 Subject: [PATCH 409/643] add exception handle for ws server --- .../Handler/WsHandshakeExceptionHandler.php | 49 +++++++++++++++++++ .../Handler/WsMessageExceptionHandler.php | 45 +++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 app/Exception/Handler/WsHandshakeExceptionHandler.php create mode 100644 app/Exception/Handler/WsMessageExceptionHandler.php diff --git a/app/Exception/Handler/WsHandshakeExceptionHandler.php b/app/Exception/Handler/WsHandshakeExceptionHandler.php new file mode 100644 index 00000000..53a69ee1 --- /dev/null +++ b/app/Exception/Handler/WsHandshakeExceptionHandler.php @@ -0,0 +1,49 @@ +withStatus(500)->withContent(sprintf( + '%s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine() + )); + } + + $data = [ + 'code' => $e->getCode(), + 'error' => sprintf('(%s) %s', get_class($e), $e->getMessage()), + 'file' => sprintf('At %s line %d', $e->getFile(), $e->getLine()), + 'trace' => $e->getTraceAsString(), + ]; + + // Debug is true + return $response->withData($data); + } +} diff --git a/app/Exception/Handler/WsMessageExceptionHandler.php b/app/Exception/Handler/WsMessageExceptionHandler.php new file mode 100644 index 00000000..1b533f6c --- /dev/null +++ b/app/Exception/Handler/WsMessageExceptionHandler.php @@ -0,0 +1,45 @@ +getMessage(), $e->getFile(), $e->getLine()); + + Log::error('Ws server error(%s)', $message); + + // Debug is false + if (!APP_DEBUG) { + server()->push($frame->fd, $e->getMessage()); + return; + } + + server()->push($frame->fd, $message); + } +} From 019404c0b1d55f357744104e00ba0b31eb9ca904 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 28 Jun 2019 16:19:44 +0800 Subject: [PATCH 410/643] update fix error --- app/Validator/TestValidator.php | 1 + dev.composer.json | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Validator/TestValidator.php b/app/Validator/TestValidator.php index 6113bfda..1f7d99f5 100644 --- a/app/Validator/TestValidator.php +++ b/app/Validator/TestValidator.php @@ -31,6 +31,7 @@ class TestValidator protected $type; /** + * @IsString() * @AlphaDash(message="Passwords can only be alphabet, numbers, dashes, underscores") * * @var string diff --git a/dev.composer.json b/dev.composer.json index 758b4856..6dd6f8c0 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -35,7 +35,6 @@ "SwoftTest\\": "./test/" } }, - "minimum-stability": "dev", "scripts": { "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" From b4b29c66d41d56bbe07a76984a0cd1d2ed0868bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Sat, 29 Jun 2019 12:54:47 +0800 Subject: [PATCH 411/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64077824..d183439a 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ ## Document - [中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) -- [English](https://www.swoft.org/docs/2.x/en) +- [English](https://en.swoft.org/docs) QQ Group1: 548173319 QQ Group2: 778656850 From 2fde753ec2a8b7d0607506cdeabd0b2ebf4f7d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Sat, 29 Jun 2019 22:23:34 +0800 Subject: [PATCH 412/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d183439a..d4dfde82 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

      -[![Latest Version](https://img.shields.io/badge/version-v2.0.1-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Latest Version](https://img.shields.io/badge/version-v2.0.2-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) From f5184df84aac321ffeedd4509aaa1553db74fbb3 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 30 Jun 2019 23:59:40 +0800 Subject: [PATCH 413/643] Add apollo demo --- app/Console/Command/ApolloCommand.php | 63 +++++++++++++++++++++++++++ app/Model/Logic/ApolloLogic.php | 37 ++++++++++++++++ app/Validator/CustomerValidator.php | 2 + app/Validator/Rule/AlphaDashRule.php | 2 + app/Validator/TestValidator.php | 1 + app/bean.php | 4 ++ 6 files changed, 109 insertions(+) create mode 100644 app/Console/Command/ApolloCommand.php create mode 100644 app/Model/Logic/ApolloLogic.php diff --git a/app/Console/Command/ApolloCommand.php b/app/Console/Command/ApolloCommand.php new file mode 100644 index 00000000..d758b661 --- /dev/null +++ b/app/Console/Command/ApolloCommand.php @@ -0,0 +1,63 @@ +config->listen($namespaces, [$this, 'updateConfigFile']); + } + + /** + * @param array $data + */ + public function updateConfigFile(array $data): void + { + foreach ($data as $namespace => $namespaceData) { + $configFile = sprintf('@config/%s.php', $namespace); + + $configKVs = $namespaceData['configurations'] ?? ''; + $content = 'config->pull('application'); + + // Print data + var_dump($data); + } +} \ No newline at end of file diff --git a/app/Validator/CustomerValidator.php b/app/Validator/CustomerValidator.php index a832c752..c97e358a 100644 --- a/app/Validator/CustomerValidator.php +++ b/app/Validator/CustomerValidator.php @@ -29,9 +29,11 @@ public function validate(array $data, array $params): array if ($start === null && $end === null) { throw new ValidatorException('Start time and end time cannot be empty'); } + if ($start > $end) { throw new ValidatorException('Start cannot be greater than the end time'); } + return $data; } } diff --git a/app/Validator/Rule/AlphaDashRule.php b/app/Validator/Rule/AlphaDashRule.php index 8180b12d..5a920095 100644 --- a/app/Validator/Rule/AlphaDashRule.php +++ b/app/Validator/Rule/AlphaDashRule.php @@ -30,10 +30,12 @@ public function validate(array $data, string $propertyName, $item, $default = nu $message = (empty($message)) ? sprintf('%s must exist!', $propertyName) : $message; throw new ValidatorException($message); } + $rule = '/^[A-Za-z0-9\-\_]+$/'; if (preg_match($rule, $data[$propertyName])) { return [$data]; } + $message = (empty($message)) ? sprintf('%s must be a email', $propertyName) : $message; throw new ValidatorException($message); } diff --git a/app/Validator/TestValidator.php b/app/Validator/TestValidator.php index 6113bfda..1f7d99f5 100644 --- a/app/Validator/TestValidator.php +++ b/app/Validator/TestValidator.php @@ -31,6 +31,7 @@ class TestValidator protected $type; /** + * @IsString() * @AlphaDash(message="Passwords can only be alphabet, numbers, dashes, underscores") * * @var string diff --git a/app/bean.php b/app/bean.php index 995314ff..99fd9e88 100644 --- a/app/bean.php +++ b/app/bean.php @@ -107,4 +107,8 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], + 'apollo' => [ + 'host' => '192.168.2.102', + 'timeout' => -1 + ] ]; From be5d89123c9fe3a4d5b5b9e9af23045d5d3d8d01 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 1 Jul 2019 21:16:48 +0800 Subject: [PATCH 414/643] Update demo --- .../{ApolloCommand.php => AgentCommand.php} | 39 ++++++++++++++----- app/bean.php | 2 +- 2 files changed, 31 insertions(+), 10 deletions(-) rename app/Console/Command/{ApolloCommand.php => AgentCommand.php} (57%) diff --git a/app/Console/Command/ApolloCommand.php b/app/Console/Command/AgentCommand.php similarity index 57% rename from app/Console/Command/ApolloCommand.php rename to app/Console/Command/AgentCommand.php index d758b661..86fec6e1 100644 --- a/app/Console/Command/ApolloCommand.php +++ b/app/Console/Command/AgentCommand.php @@ -11,16 +11,20 @@ use Swoft\Co; use Swoft\Console\Annotation\Mapping\Command; use Swoft\Console\Annotation\Mapping\CommandMapping; +use Swoft\Http\Server\HttpServer; use Swoft\Log\Helper\CLog; +use Swoft\Rpc\Server\ServiceServer; +use Swoft\WebSocket\Server\WebSocketServer; +use Throwable; /** - * Class ApolloCommand + * Class AgentCommand * * @since 2.0 * - * @Command("apollo") + * @Command("agent") */ -class ApolloCommand +class AgentCommand { /** * @Inject() @@ -31,10 +35,6 @@ class ApolloCommand /** * @CommandMapping(name="index") - * - * @throws ReflectionException - * @throws ApolloException - * @throws ContainerException */ public function index(): void { @@ -42,11 +42,20 @@ public function index(): void 'application' ]; - $this->config->listen($namespaces, [$this, 'updateConfigFile']); + while (true) { + try { + $this->config->listen($namespaces, [$this, 'updateConfigFile']); + } catch (Throwable $e) { + CLog::error('Config agent fail(%s %s %d)!', $e->getMessage(), $e->getFile(), $e->getLine()); + } + } } /** * @param array $data + * + * @throws ContainerException + * @throws ReflectionException */ public function updateConfigFile(array $data): void { @@ -57,7 +66,19 @@ public function updateConfigFile(array $data): void $content = 'restart(); + +// /** @var ServiceServer $server */ +// $server = bean('rpcServer'); +// $server->restart(); + + /* @var WebSocketServer $server */ + $server = bean('wsServer'); + $server->restart(); } } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 99fd9e88..c498b2cf 100644 --- a/app/bean.php +++ b/app/bean.php @@ -108,7 +108,7 @@ ], ], 'apollo' => [ - 'host' => '192.168.2.102', + 'host' => '192.168.4.11', 'timeout' => -1 ] ]; From cb2cacfc4e9b05b85a4b96d7e25fcdb164d671fd Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 2 Jul 2019 19:36:30 +0800 Subject: [PATCH 415/643] update readme --- README.md | 60 +++++++++---------- README.zh-CN.md | 89 ++++++++++++++++++++++++++++ app/Console/Command/TestCommand.php | 49 +++++++++++++++ app/bean.php | 3 + public/image/start-http-server.jpg | Bin 0 -> 74998 bytes 5 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 README.zh-CN.md create mode 100644 public/image/start-http-server.jpg diff --git a/README.md b/README.md index 64077824..f5170a17 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,12 @@ [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +![](public/image/start-http-server.jpg) + ⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole +> **[中文说明](README.zh-CN.md)** + ## Feature - Built-in high performance network server(Http/Websocket/RPC) @@ -37,53 +41,45 @@ ## Document -- [中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) - [English](https://www.swoft.org/docs/2.x/en) -QQ Group1: 548173319 -QQ Group2: 778656850 +## Discuss + +- [swoft-cloud/community](https://gitter.im/swoft-cloud/community) ## Requirement -- [PHP 7.1 +](https://github.com/php/php-src/releases) -- [Swoole 4.3.4 + ](https://github.com/swoole/swoole-src/releases) +- [PHP 7.1+](https://github.com/php/php-src/releases) +- [Swoole 4.3.4+](https://github.com/swoole/swoole-src/releases) - [Composer](https://getcomposer.org/) ## Install ### Composer -* `composer create-project swoft/swoft swoft` +```bash +composer create-project swoft/swoft swoft +``` ## Start -```text +- Http server + +```bash [root@swoft swoft]# php bin/swoft http:start -2019/06/02-11:18:06 [INFO] Swoole\Runtime::enableCoroutine -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @base=/data/www/swoft -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @app=@base/app -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @config=@base/config -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @runtime=@base/runtime -2019/06/02-11:18:06 [INFO] Project path is /data/www/swoft -2019/06/02-11:18:06 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Env file(/data/www/swoft/.env) is loaded -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Annotations is scanned(autoloader 23, annotation 226, parser 57) -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) config path=/data/www/swoft/config -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) config env= -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Bean is initialized(singleton 144, prototype 41, definition 30) -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Event manager initialized(30 listener, 3 subscriber) -2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) WebSocket server route registered(module 2, message command 3) -2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) Error manager init completed(2 type, 3 handler, 3 exception) -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Console command route registered (group 14, command 5) - Information Panel - *********************************************************************** - * HTTP | Listen: 0.0.0.0:18306, type: TCP, mode: Process, worker: 1 - * rpc | Listen: 0.0.0.0:18307, type: TCP - *********************************************************************** - -HTTP server start success ! -2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) Registered swoole events: - start, shutdown, managerStart, managerStop, workerStart, workerStop, workerError, request, task, finish -Server start success (Master PID: 249, Manager PID: 250) +``` + +- WebSocket server + +```bash +[root@swoft swoft]# php bin/swoft ws:start +``` + +- RPC server + +```bash +[root@swoft swoft]# php bin/swoft rpc:start ``` ## License diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 00000000..8bcb6a39 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,89 @@ +

      + + swoft + +

      + +[![Latest Version](https://img.shields.io/badge/version-v2.0.1-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) +[![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) +[![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) +[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) + +![](public/image/start-http-server.jpg) + +⚡️ 现代的高性能的 AOP & Coroutine PHP框架 + +> **[EN README](README.md)** + +## 功能特色 + + - 内置高性能网络服务器(Http/Websocket/RPC) + - 灵活的组件功能 + - 强大的注解功能 + - 多样化的命令终端(控制台) + - 强大的面向切面编程(AOP) + - 容器管理,依赖注入(DI) + - 灵活的事件机制 + - 基于PSR-7的HTTP消息的实现 + - 基于PSR-14的事件管理 + - 基于PSR-15的中间件 + - 国际化(i18n)支持 + - 简单有效的参数验证器 + - 高性能连接池(Mysql/Redis/RPC),自动重新连接 + - 数据库高度兼容Laravel的使用方式 + - Redis高度兼容Laravel的使用方式 + - 高效的任务处理 + - 灵活的异常处理 + - 强大的日志系统 + +## 在线文档 + +- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [English](https://www.swoft.org/docs/2.x/en) + +## 学习交流 + +- QQ Group1: 548173319 +- QQ Group2: 778656850 +- [swoft-cloud/community](https://gitter.im/swoft-cloud/community) + +## Requirement + +- [PHP 7.1+](https://github.com/php/php-src/releases) +- [Swoole 4.3.4+](https://github.com/swoole/swoole-src/releases) +- [Composer](https://getcomposer.org/) + +## Install + +### Composer + +```bash +composer create-project swoft/swoft swoft +``` + +## Start + +- Http server + +```bash +[root@swoft swoft]# php bin/swoft http:start +``` + +- WebSocket server + +```bash +[root@swoft swoft]# php bin/swoft ws:start +``` + +- RPC server + +```bash +[root@swoft swoft]# php bin/swoft rpc:start +``` + +## License + +Swoft is an open-source software licensed under the [LICENSE](LICENSE) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 5e667469..9d7fa393 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -8,9 +8,13 @@ use function input; use function output; use function sprintf; +use Swoft\Console\Annotation\Mapping\CommandOption; use Swoft\Console\Exception\ConsoleErrorException; use Swoft\Console\Helper\Show; +use Swoft\Console\Input\Input; +use Swoft\Console\Output\Output; use Swoft\Http\Server\Router\Route; +use Swoole\Client; /** * Class TestCommand @@ -138,4 +142,49 @@ public function error(): void { ConsoleErrorException::throw('this is an error message'); } + + /** + * @CommandMapping(desc="connect to an tcp server and allow send message interactive") + * @CommandOption("host", short="H", desc="the tcp server host address", default="127.0.0.1", type="string") + * @CommandOption("port", short="p", desc="the tcp server port number", default="18309", type="integer") + * + * @param Input $input + * @param Output $output + */ + public function tcp(Input $input, Output $output): void + { + $cli = new Client(\SWOOLE_SOCK_TCP); + $host = $input->getSameOpt(['host', 'H'], '127.0.0.1'); + $port = $input->getSameOpt(['port', 'p'], 18309); + + if (!$ok = $cli->connect((string)$host, (int)$port, 5.0)) { + $code = $cli->errCode; + $msg = socket_strerror($code); + Show::error("Connect failed. Error($code): $msg"); + return; + } + + $addr = $host . ':' . $port; + $output->colored('Successful connect to tcp server ' . $addr, 'success'); + + while (true) { + if (!$msg = $input->read('> ')) { + $output->liteWarning('Please input message for send'); + continue; + } + + // Exit interactive terminal + if ($msg === 'quit' || $msg === 'exit') { + $output->colored('Quit, Bye!'); + break; + } + + $cli->send($msg); + + $res = $cli->recv(); + $output->writef('Return: %s', $res); + } + + $cli->close(); + } } diff --git a/app/bean.php b/app/bean.php index 995314ff..b522d559 100644 --- a/app/bean.php +++ b/app/bean.php @@ -107,4 +107,7 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], + 'cliRouter' => [ + // 'disabledGroups' => ['demo', 'test'], + ], ]; diff --git a/public/image/start-http-server.jpg b/public/image/start-http-server.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e38f23a56b15db764c9de70b55e3d08780faa1b GIT binary patch literal 74998 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 literal 0 HcmV?d00001 From 4bb4acc2f7cfaaddba30d7f8c801c9048be1d101 Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 2 Jul 2019 19:48:25 +0800 Subject: [PATCH 416/643] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f5170a17..08a5a8f6 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ ## Discuss - [swoft-cloud/community](https://gitter.im/swoft-cloud/community) +- QQ Group1: 548173319 +- QQ Group2: 778656850 ## Requirement From bf269907f8bb4b0c72f8290b1b82b04ad6f1e075 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Tue, 2 Jul 2019 22:22:31 +0800 Subject: [PATCH 417/643] add database test --- app/Common/DbSelector.php | 2 +- app/Console/Command/TestCommand.php | 5 +- app/Http/Controller/DbBuilderController.php | 20 ++- app/Http/Controller/DbModelController.php | 41 ++++- app/Http/Controller/RedisController.php | 21 ++- app/Migration/AddMsg20190630164222.php | 52 ++++++ app/Migration/AddUser20190627225524.php | 58 +++++++ app/Migration/Message20190627225525.php | 50 ++++++ app/Model/Entity/Count.php | 72 +++++---- app/Model/Entity/User.php | 168 +++++++++++++++----- app/bean.php | 29 ++-- 11 files changed, 428 insertions(+), 90 deletions(-) create mode 100644 app/Migration/AddMsg20190630164222.php create mode 100644 app/Migration/AddUser20190627225524.php create mode 100644 app/Migration/Message20190627225525.php diff --git a/app/Common/DbSelector.php b/app/Common/DbSelector.php index b71fcad0..ea157eda 100644 --- a/app/Common/DbSelector.php +++ b/app/Common/DbSelector.php @@ -36,4 +36,4 @@ public function select(Connection $connection): void $dbName = sprintf('%s%s', $createDbName, (string)$selectIndex); $connection->db($dbName); } -} \ No newline at end of file +} diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index c3cb2f5b..be861c39 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -61,6 +61,7 @@ private function uris(): array '/redis/ep', '/redis/release', '/redis/poolSet', + '/redis/set', ], 'log' => [ '/log/test' @@ -77,13 +78,15 @@ private function uris(): array '/dbModel/update', '/dbModel/delete', '/dbModel/save', + '/dbModel/batchUpdate', '/selectDb/modelNotExistDb', '/selectDb/queryNotExistDb', '/selectDb/dbNotExistDb', '/selectDb/modelDb', '/selectDb/queryDb', '/selectDb/dbDb', - '/selectDb/select' + '/selectDb/select', + '/builder/schema' ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php index 93b5539d..d0ec77bc 100644 --- a/app/Http/Controller/DbBuilderController.php +++ b/app/Http/Controller/DbBuilderController.php @@ -3,12 +3,30 @@ namespace App\Http\Controller; +use Swoft\Db\Schema\Blueprint; +use Swoft\Db\Schema; +use Swoft\Http\Server\Annotation\Mapping\Controller; +use Swoft\Http\Server\Annotation\Mapping\RequestMapping; + /** * Class DbBuilderController * * @since 2.0 + * + * @Controller("builder") */ class DbBuilderController { -} \ No newline at end of file + /** + * @RequestMapping() + * + * @return void + */ + public function schema() + { + Schema::createIfNotExists('test', function (Blueprint $blueprint) { + $blueprint->increments('id'); + }); + } +} diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 646efa03..42620bf0 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -65,7 +65,7 @@ public function update(): array { $id = $this->getId(); - User::updateOrInsert(['id'=>$id], ['name'=> 'swoft']); + User::updateOrInsert(['id' => $id], ['name' => 'swoft']); $user = User::find($id); @@ -81,7 +81,7 @@ public function update(): array */ public function delete(): array { - $id = $this->getId(); + $id = $this->getId(); $result = User::find($id)->delete(); return [$result]; @@ -101,4 +101,39 @@ public function getId(): int return $user->getId(); } -} \ No newline at end of file + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function batchUpdate() + { + // User::truncate(); + User::updateOrCreate(['id' => 1], ['age' => 23]); + User::updateOrCreate(['id' => 2], ['age' => 23]); + + $values = [ + ['id' => 1, 'age' => 18], + ['id' => 2, 'age' => 19], + ]; + $values = array_column($values, null, 'id'); + + User::batchUpdateByIds($values); + + $users = User::find(array_column($values, 'id')); + + $updateResults = []; + /* @var User $user */ + foreach ($users as $user) { + $updateResults[$user->getId()] = true; + if ($user->getAge() != $values[$user->getId()]['age']) { + $updateResults[$user->getId()] = false; + } + } + + + return $updateResults; + } +} diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index e4cbca46..f30d19db 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -31,7 +31,7 @@ class RedisController /** * @RequestMapping("poolSet") */ - public function set(): array + public function poolSet(): array { $key = 'key'; $value = uniqid(); @@ -43,6 +43,25 @@ public function set(): array return [$get, $value]; } + /** + * @RequestMapping() + */ + public function set(): array + { + $key = 'key'; + $value = uniqid(); + + $this->redis->zAdd($key, [ + 'add' => 11.1, + 'score2' => 11.1, + 'score3' => 11.21 + ]); + + $get = $this->redis->sMembers($key); + + return [$get, $value]; + } + /** * @RequestMapping("str") diff --git a/app/Migration/AddMsg20190630164222.php b/app/Migration/AddMsg20190630164222.php new file mode 100644 index 00000000..772a2ad4 --- /dev/null +++ b/app/Migration/AddMsg20190630164222.php @@ -0,0 +1,52 @@ +execute($sql); + } + + /** + * @return void + */ + public function down() + { + $truncate = <<execute($truncate); + + $dropSql = <<execute($dropSql); + } +} diff --git a/app/Migration/AddUser20190627225524.php b/app/Migration/AddUser20190627225524.php new file mode 100644 index 00000000..46d73128 --- /dev/null +++ b/app/Migration/AddUser20190627225524.php @@ -0,0 +1,58 @@ +increments('id'); + $blueprint->smallInteger('age'); + $blueprint->string('label', 10); + $blueprint->integer('balance'); + }); + + Schema::getSchemaBuilder('db.pool')->table('user', function (Blueprint $blueprint) { + $blueprint->comment('base user tables'); + }); + } + + /** + * @return void + * + * @throws ReflectionException + * @throws ContainerException + * @throws DbException + */ + public function down() + { + Schema::dropIfExists('users'); + + Schema::getSchemaBuilder('db.pool')->dropIfExists('user'); + } +} diff --git a/app/Migration/Message20190627225525.php b/app/Migration/Message20190627225525.php new file mode 100644 index 00000000..44904882 --- /dev/null +++ b/app/Migration/Message20190627225525.php @@ -0,0 +1,50 @@ +schema->createIfNotExists('messages', function (Blueprint $blueprint) { + $blueprint->increments('id'); + $blueprint->text('content'); + $blueprint->timestamps(); + }); + } + + /** + * @return void + * + * @throws ReflectionException + * @throws ContainerException + * @throws DbException + */ + public function down() + { + $this->schema->dropIfExists('messages'); + } +} diff --git a/app/Model/Entity/Count.php b/app/Model/Entity/Count.php index b646a514..e43c6b69 100644 --- a/app/Model/Entity/Count.php +++ b/app/Model/Entity/Count.php @@ -8,7 +8,9 @@ use Swoft\Db\Annotation\Mapping\Id; use Swoft\Db\Eloquent\Model; + /** + * * Class Count * * @since 2.0 @@ -18,96 +20,104 @@ class Count extends Model { /** - * @Id(incrementing=true) - * - * @Column(name="id", prop="id") + * + * @Id() + * @Column() * @var int|null */ private $id; /** + * + * * @Column(name="user_id", prop="userId") * @var int|null */ private $userId; /** - * @Column(name="create_time", prop="createTime") + * * + * @Column(name="create_time", prop="createTime") * @var int|null */ private $createTime; /** - * attributes + * * * @Column() - * * @var string|null */ private $attributes; + /** - * @return null|int + * @param int|null $id + * @return void */ - public function getId(): ?int + public function setId(?int $id): void { - return $this->id; + $this->id = $id; } /** - * @param null|int $id + * @param int|null $userId + * @return void */ - public function setId(?int $id): void + public function setUserId(?int $userId): void { - $this->id = $id; + $this->userId = $userId; } /** - * @return null|int + * @param int|null $createTime + * @return void */ - public function getUserId(): ?int + public function setCreateTime(?int $createTime): void { - return $this->userId; + $this->createTime = $createTime; } /** - * @param null|int $userId + * @param string|null $attributes + * @return void */ - public function setUserId(?int $userId): void + public function setAttributes(?string $attributes): void { - $this->userId = $userId; + $this->attributes = $attributes; } /** - * @return null|int + * @return int|null */ - public function getCreateTime(): ?int + public function getId(): ?int { - return $this->createTime; + return $this->id; } /** - * @param null|int $createTime + * @return int|null */ - public function setCreateTime(?int $createTime): void + public function getUserId(): ?int { - $this->createTime = $createTime; + return $this->userId; } /** - * @return null|string + * @return int|null */ - public function getAttributes(): ?string + public function getCreateTime(): ?int { - return $this->attributes; + return $this->createTime; } /** - * @param null|string $attributes + * @return string|null */ - public function setAttributes(?string $attributes): void + public function getAttributes(): ?string { - $this->attributes = $attributes; + return $this->attributes; } -} \ No newline at end of file + +} diff --git a/app/Model/Entity/User.php b/app/Model/Entity/User.php index 19c7507f..b465fc24 100644 --- a/app/Model/Entity/User.php +++ b/app/Model/Entity/User.php @@ -10,6 +10,7 @@ /** + * * Class User * * @since 2.0 @@ -19,48 +20,73 @@ class User extends Model { /** + * * @Id() - * - * @Column(name="id", prop="id") + * @Column() * @var int|null */ private $id; /** + * + * * @Column() - * @var string|null + * @var int */ - private $name; + private $age; /** - * @Column(name="password", hidden=true) - * @var string|null + * + * + * @Column(hidden=true) + * @var string + */ + private $password; + + /** + * + * + * @Column(name="user_desc", prop="userDesc") + * @var string */ - private $pwd; + private $userDesc; /** + * + * * @Column() + * @var int|null + */ + private $add; + + /** + * * + * @Column() * @var int|null */ - private $age; + private $hahh; /** - * @Column(name="user_desc", prop="udesc") - * @var string|null + * + * + * @Column(name="test_json", prop="testJson") + * @var array|null */ - private $userDesc; + private $testJson; /** - * @return int|null + * + * + * @Column() + * @var string */ - public function getId(): ?int - { - return $this->id; - } + private $name; + /** * @param int|null $id + * @return void */ public function setId(?int $id): void { @@ -68,66 +94,130 @@ public function setId(?int $id): void } /** - * @return int|null + * @param int $age + * @return void */ - public function getAge(): ?int + public function setAge(int $age): void { - return $this->age; + $this->age = $age; } /** - * @param int|null $age + * @param string $password + * @return void */ - public function setAge(?int $age): void + public function setPassword(string $password): void { - $this->age = $age; + $this->password = $password; } /** - * @return string|null + * @param string $userDesc + * @return void */ - public function getName(): ?string + public function setUserDesc(string $userDesc): void { - return $this->name; + $this->userDesc = $userDesc; + } + + /** + * @param int|null $add + * @return void + */ + public function setAdd(?int $add): void + { + $this->add = $add; } /** - * @param string|null $name + * @param int|null $hahh + * @return void */ - public function setName(?string $name): void + public function setHahh(?int $hahh): void + { + $this->hahh = $hahh; + } + + /** + * @param array|null $testJson + * @return void + */ + public function setTestJson(?array $testJson): void + { + $this->testJson = $testJson; + } + + /** + * @param string $name + * @return void + */ + public function setName(string $name): void { $this->name = $name; } /** - * @return string|null + * @return int|null + */ + public function getId(): ?int + { + return $this->id; + } + + /** + * @return int */ - public function getPwd(): ?string + public function getAge(): int { - return $this->pwd; + return $this->age; } /** - * @param string|null $pwd + * @return string */ - public function setPwd(?string $pwd): void + public function getPassword(): string { - $this->pwd = $pwd; + return $this->password; } /** - * @return string|null + * @return string */ - public function getUserDesc(): ?string + public function getUserDesc(): string { return $this->userDesc; } /** - * @param string|null $userDesc + * @return int|null */ - public function setUserDesc(?string $userDesc): void + public function getAdd(): ?int { - $this->userDesc = $userDesc; + return $this->add; + } + + /** + * @return int|null + */ + public function getHahh(): ?int + { + return $this->hahh; } -} \ No newline at end of file + + /** + * @return array|null + */ + public function getTestJson(): ?array + { + return $this->testJson; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + +} diff --git a/app/bean.php b/app/bean.php index 995314ff..0b724158 100644 --- a/app/bean.php +++ b/app/bean.php @@ -15,12 +15,12 @@ use Swoft\Redis\RedisDb; return [ - 'logger' => [ + 'logger' => [ 'flushRequest' => true, 'enable' => false, 'json' => false, ], - 'httpServer' => [ + 'httpServer' => [ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ @@ -36,47 +36,50 @@ 'task_enable_coroutine' => true ] ], - 'httpDispatcher' => [ + 'httpDispatcher' => [ // Add global http middleware 'middlewares' => [ // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], ], - 'db' => [ + 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', ], - 'db2' => [ + 'db2' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, 'database' => bean('db2') ], - 'db3' => [ + 'db3' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456' ], - 'db3.pool' => [ + 'db3.pool' => [ 'class' => Pool::class, 'database' => bean('db3') ], - 'redis' => [ + 'migrationManager' => [ + 'migrationPath' => '@app/Migration', + ], + 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ], - 'user' => [ + 'user' => [ 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, @@ -88,14 +91,14 @@ ], 'packet' => bean('rpcClientPacket') ], - 'user.pool' => [ + 'user.pool' => [ 'class' => ServicePool::class, 'client' => bean('user') ], - 'rpcServer' => [ + 'rpcServer' => [ 'class' => ServiceServer::class, ], - 'wsServer' => [ + 'wsServer' => [ 'class' => WebSocketServer::class, 'on' => [ // Enable http handle From b87afdc17cdba23909c3dcf04732308165ec6e16 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 2 Jul 2019 22:41:22 +0800 Subject: [PATCH 418/643] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7bb91fef..fd131c0b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

      -[![Latest Version](https://img.shields.io/badge/version-v2.0.2-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) @@ -41,11 +41,9 @@ ## Document -<<<<<<< HEAD -- [中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) - [English](https://en.swoft.org/docs) - ## Discuss - [swoft-cloud/community](https://gitter.im/swoft-cloud/community) From 87c86c7d5bfc2d29859a36c67ec59baf364abe07 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 2 Jul 2019 22:42:56 +0800 Subject: [PATCH 419/643] Update README.zh-CN.md --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 8bcb6a39..bc55cb1a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -4,7 +4,7 @@

      -[![Latest Version](https://img.shields.io/badge/version-v2.0.1-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) From d1156c9f63ec075314c58121fd88888923ad6c48 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 3 Jul 2019 14:31:20 +0800 Subject: [PATCH 420/643] Add ConsulLogic --- app/Model/Logic/ConsulLogic.php | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 app/Model/Logic/ConsulLogic.php diff --git a/app/Model/Logic/ConsulLogic.php b/app/Model/Logic/ConsulLogic.php new file mode 100644 index 00000000..acf7b386 --- /dev/null +++ b/app/Model/Logic/ConsulLogic.php @@ -0,0 +1,77 @@ +kv->put('/test/my/key', $value); + + $response = $this->kv->get('/test/my/key'); + var_dump($response->getBody(), $response->getResult()); + } +} \ No newline at end of file From 2058e87e006608c7a31908adf47ec3cdcf37f214 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 3 Jul 2019 16:30:50 +0800 Subject: [PATCH 421/643] Add consul demo --- app/Listener/DeregisterServiceListener.php | 45 +++++++++++++++ app/Listener/RegisterServiceListener.php | 67 ++++++++++++++++++++++ app/bean.php | 33 ++++++----- 3 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 app/Listener/DeregisterServiceListener.php create mode 100644 app/Listener/RegisterServiceListener.php diff --git a/app/Listener/DeregisterServiceListener.php b/app/Listener/DeregisterServiceListener.php new file mode 100644 index 00000000..bae52825 --- /dev/null +++ b/app/Listener/DeregisterServiceListener.php @@ -0,0 +1,45 @@ +getTarget(); + +// $scheduler = Swoole\Coroutine\Scheduler(); +// $scheduler->add(function () use ($httpServer) { +// $this->agent->deregisterService('swoft'); +// }); +// $scheduler->start(); + } +} \ No newline at end of file diff --git a/app/Listener/RegisterServiceListener.php b/app/Listener/RegisterServiceListener.php new file mode 100644 index 00000000..101cb596 --- /dev/null +++ b/app/Listener/RegisterServiceListener.php @@ -0,0 +1,67 @@ +getTarget(); + + $service = [ + 'ID' => 'swoft', + 'Name' => 'swoft', + 'Tags' => [ + 'http' + ], + 'Address' => '127.0.0.1', + 'Port' => $httpServer->getPort(), + 'Meta' => [ + 'version' => '1.0' + ], + 'EnableTagOverride' => false, + 'Weights' => [ + 'Passing' => 10, + 'Warning' => 1 + ] + ]; + + +// $scheduler = Swoole\Coroutine\Scheduler(); +// $scheduler->add(function () use ($service) { +// // Register +// $this->agent->registerService($service); +// CLog::info('Swoft http register service success by consul!'); +// }); +// $scheduler->start(); + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 279e55c3..579d4dfa 100644 --- a/app/bean.php +++ b/app/bean.php @@ -15,12 +15,12 @@ use Swoft\Redis\RedisDb; return [ - 'logger' => [ + 'logger' => [ 'flushRequest' => true, 'enable' => false, 'json' => false, ], - 'httpServer' => [ + 'httpServer' => [ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ @@ -36,50 +36,50 @@ 'task_enable_coroutine' => true ] ], - 'httpDispatcher' => [ + 'httpDispatcher' => [ // Add global http middleware 'middlewares' => [ // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], ], - 'db' => [ + 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', ], - 'db2' => [ + 'db2' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, 'database' => bean('db2') ], - 'db3' => [ + 'db3' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', 'username' => 'root', 'password' => 'swoft123456' ], - 'db3.pool' => [ + 'db3.pool' => [ 'class' => Pool::class, 'database' => bean('db3') ], 'migrationManager' => [ 'migrationPath' => '@app/Migration', ], - 'redis' => [ + 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ], - 'user' => [ + 'user' => [ 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, @@ -91,14 +91,14 @@ ], 'packet' => bean('rpcClientPacket') ], - 'user.pool' => [ + 'user.pool' => [ 'class' => ServicePool::class, 'client' => bean('user') ], - 'rpcServer' => [ + 'rpcServer' => [ 'class' => ServiceServer::class, ], - 'wsServer' => [ + 'wsServer' => [ 'class' => WebSocketServer::class, 'on' => [ // Enable http handle @@ -110,11 +110,14 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], - 'cliRouter' => [ + 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], ], - 'apollo' => [ + 'apollo' => [ 'host' => '192.168.4.11', 'timeout' => -1 + ], + 'consul' => [ + 'host' => '192.168.4.11' ] ]; From f41f37afde810134fbfe194bfb63afde7105dbcf Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 3 Jul 2019 17:45:13 +0800 Subject: [PATCH 422/643] Modify dev composer.json --- dev.composer.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dev.composer.json b/dev.composer.json index 6dd6f8c0..2782760c 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -14,9 +14,7 @@ "ext-json": "*", "ext-swoole": ">=4.3", "swoft/component": "dev-master as 2.0", - "swoft/ext": "dev-master as 2.0", - "swoft/view": "~2.0.0", - "swoft/devtool": "~2.0.0" + "swoft/ext": "dev-master as 2.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", From 5df49b25c1ffdfa972383dd007d91c0df5340c48 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 4 Jul 2019 12:35:58 +0800 Subject: [PATCH 423/643] update readme --- README.md | 64 ++++++++++----------- README.zh-CN.md | 89 +++++++++++++++++++++++++++++ public/image/start-http-server.jpg | Bin 0 -> 74998 bytes 3 files changed, 120 insertions(+), 33 deletions(-) create mode 100644 README.zh-CN.md create mode 100644 public/image/start-http-server.jpg diff --git a/README.md b/README.md index d4dfde82..fd131c0b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

      -[![Latest Version](https://img.shields.io/badge/version-v2.0.2-green.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/releases) +[![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) @@ -12,8 +12,12 @@ [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +![](public/image/start-http-server.jpg) + ⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole +> **[中文说明](README.zh-CN.md)** + ## Feature - Built-in high performance network server(Http/Websocket/RPC) @@ -37,53 +41,47 @@ ## Document -- [中文](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) - [English](https://en.swoft.org/docs) -QQ Group1: 548173319 -QQ Group2: 778656850 +## Discuss + +- [swoft-cloud/community](https://gitter.im/swoft-cloud/community) +- QQ Group1: 548173319 +- QQ Group2: 778656850 ## Requirement -- [PHP 7.1 +](https://github.com/php/php-src/releases) -- [Swoole 4.3.4 + ](https://github.com/swoole/swoole-src/releases) +- [PHP 7.1+](https://github.com/php/php-src/releases) +- [Swoole 4.3.4+](https://github.com/swoole/swoole-src/releases) - [Composer](https://getcomposer.org/) ## Install ### Composer -* `composer create-project swoft/swoft swoft` +```bash +composer create-project swoft/swoft swoft +``` ## Start -```text +- Http server + +```bash [root@swoft swoft]# php bin/swoft http:start -2019/06/02-11:18:06 [INFO] Swoole\Runtime::enableCoroutine -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @base=/data/www/swoft -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @app=@base/app -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @config=@base/config -2019/06/02-11:18:06 [INFO] Swoft\SwoftApplication:__construct(14) Set alias @runtime=@base/runtime -2019/06/02-11:18:06 [INFO] Project path is /data/www/swoft -2019/06/02-11:18:06 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Env file(/data/www/swoft/.env) is loaded -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Annotations is scanned(autoloader 23, annotation 226, parser 57) -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) config path=/data/www/swoft/config -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) config env= -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Bean is initialized(singleton 144, prototype 41, definition 30) -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Event manager initialized(30 listener, 3 subscriber) -2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) WebSocket server route registered(module 2, message command 3) -2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) Error manager init completed(2 type, 3 handler, 3 exception) -2019/06/02-11:18:11 [INFO] Swoft\Processor\ApplicationProcessor:handle(221) Console command route registered (group 14, command 5) - Information Panel - *********************************************************************** - * HTTP | Listen: 0.0.0.0:18306, type: TCP, mode: Process, worker: 1 - * rpc | Listen: 0.0.0.0:18307, type: TCP - *********************************************************************** - -HTTP server start success ! -2019/06/02-11:18:11 [INFO] Swoft\Event\Manager\EventManager:triggerListeners(324) Registered swoole events: - start, shutdown, managerStart, managerStop, workerStart, workerStop, workerError, request, task, finish -Server start success (Master PID: 249, Manager PID: 250) +``` + +- WebSocket server + +```bash +[root@swoft swoft]# php bin/swoft ws:start +``` + +- RPC server + +```bash +[root@swoft swoft]# php bin/swoft rpc:start ``` ## License diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 00000000..bc55cb1a --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,89 @@ +

      + + swoft + +

      + +[![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) +[![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) +[![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) +[![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) +[![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) + +![](public/image/start-http-server.jpg) + +⚡️ 现代的高性能的 AOP & Coroutine PHP框架 + +> **[EN README](README.md)** + +## 功能特色 + + - 内置高性能网络服务器(Http/Websocket/RPC) + - 灵活的组件功能 + - 强大的注解功能 + - 多样化的命令终端(控制台) + - 强大的面向切面编程(AOP) + - 容器管理,依赖注入(DI) + - 灵活的事件机制 + - 基于PSR-7的HTTP消息的实现 + - 基于PSR-14的事件管理 + - 基于PSR-15的中间件 + - 国际化(i18n)支持 + - 简单有效的参数验证器 + - 高性能连接池(Mysql/Redis/RPC),自动重新连接 + - 数据库高度兼容Laravel的使用方式 + - Redis高度兼容Laravel的使用方式 + - 高效的任务处理 + - 灵活的异常处理 + - 强大的日志系统 + +## 在线文档 + +- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [English](https://www.swoft.org/docs/2.x/en) + +## 学习交流 + +- QQ Group1: 548173319 +- QQ Group2: 778656850 +- [swoft-cloud/community](https://gitter.im/swoft-cloud/community) + +## Requirement + +- [PHP 7.1+](https://github.com/php/php-src/releases) +- [Swoole 4.3.4+](https://github.com/swoole/swoole-src/releases) +- [Composer](https://getcomposer.org/) + +## Install + +### Composer + +```bash +composer create-project swoft/swoft swoft +``` + +## Start + +- Http server + +```bash +[root@swoft swoft]# php bin/swoft http:start +``` + +- WebSocket server + +```bash +[root@swoft swoft]# php bin/swoft ws:start +``` + +- RPC server + +```bash +[root@swoft swoft]# php bin/swoft rpc:start +``` + +## License + +Swoft is an open-source software licensed under the [LICENSE](LICENSE) diff --git a/public/image/start-http-server.jpg b/public/image/start-http-server.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1e38f23a56b15db764c9de70b55e3d08780faa1b GIT binary patch literal 74998 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 literal 0 HcmV?d00001 From 80857016d6bc2b4249ceee4cc3d076e0f881a07d Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 4 Jul 2019 12:41:48 +0800 Subject: [PATCH 424/643] update: change composer source to aliyun --- composer.cn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.cn.json b/composer.cn.json index 1b6f3bb3..97a8c40e 100644 --- a/composer.cn.json +++ b/composer.cn.json @@ -54,7 +54,7 @@ "repositories": { "packagist": { "type": "composer", - "url": "https://packagist.laravel-china.org/" + "url": "https://mirrors.aliyun.com/composer/" } } } From cef4a3f8806aded98ee59acfa861a2b49ff3f9cf Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 5 Jul 2019 17:24:07 +0800 Subject: [PATCH 425/643] Add breaker demo --- app/Common/RpcProvider.php | 59 ++++++++++++ app/Console/Command/TestCommand.php | 21 ++-- app/Http/Controller/BreakerController.php | 71 ++++++++++++++ app/Model/Logic/BreakerLogic.php | 112 ++++++++++++++++++++++ bin/test.php | 92 +++--------------- 5 files changed, 268 insertions(+), 87 deletions(-) create mode 100644 app/Common/RpcProvider.php create mode 100644 app/Http/Controller/BreakerController.php create mode 100644 app/Model/Logic/BreakerLogic.php diff --git a/app/Common/RpcProvider.php b/app/Common/RpcProvider.php new file mode 100644 index 00000000..782fc955 --- /dev/null +++ b/app/Common/RpcProvider.php @@ -0,0 +1,59 @@ +agent->services(); + + $services = [ + + ]; + + return $services; + } +} \ No newline at end of file diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index a4fe4dc7..b73096f4 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -62,7 +62,7 @@ public function ab() private function uris(): array { return [ - 'redis' => [ + 'redis' => [ '/redis/str', '/redis/et', '/redis/ep', @@ -70,10 +70,10 @@ private function uris(): array '/redis/poolSet', '/redis/set', ], - 'log' => [ + 'log' => [ '/log/test' ], - 'db' => [ + 'db' => [ '/dbTransaction/ts', '/dbTransaction/cm', '/dbTransaction/rl', @@ -95,23 +95,28 @@ private function uris(): array '/selectDb/select', '/builder/schema' ], - 'task' => [ + 'task' => [ '/task/getListByCo', '/task/deleteByCo', '/task/getListByAsync', '/task/deleteByAsync', ], - 'rpc' => [ + 'rpc' => [ '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', '/rpc/sendBigString' ], - 'co' => [ + 'co' => [ '/co/multi' ], - 'bean' => [ + 'bean' => [ '/bean/request' + ], + 'breaker' => [ + '/breaker/unbreak', + '/breaker/breaked', + '/breaker/loopBreaker' ] ]; } @@ -156,7 +161,7 @@ public function error(): void */ public function tcp(Input $input, Output $output): void { - $cli = new Client(\SWOOLE_SOCK_TCP); + $cli = new Client(\SWOOLE_SOCK_TCP); $host = $input->getSameOpt(['host', 'H'], '127.0.0.1'); $port = $input->getSameOpt(['port', 'p'], 18309); diff --git a/app/Http/Controller/BreakerController.php b/app/Http/Controller/BreakerController.php new file mode 100644 index 00000000..96373323 --- /dev/null +++ b/app/Http/Controller/BreakerController.php @@ -0,0 +1,71 @@ +logic->func(); + } + + /** + * @RequestMapping() + * + * @return array + * @throws Exception + */ + public function unbreak(): array + { + return [$this->logic->func2()]; + } + + /** + * @RequestMapping() + * + * @return string + * @throws Exception + */ + public function loopBraker(): string + { + return $this->logic->loop(); + } + + /** + * @RequestMapping() + * + * @return string + * @throws Exception + */ + public function unFallback(): string + { + return $this->logic->unFallback(); + } +} \ No newline at end of file diff --git a/app/Model/Logic/BreakerLogic.php b/app/Model/Logic/BreakerLogic.php new file mode 100644 index 00000000..68b1cac0 --- /dev/null +++ b/app/Model/Logic/BreakerLogic.php @@ -0,0 +1,112 @@ + 10, 'universe' => 10, 'everything' => 22]; -/** - * Class AliyunSms - * - * @since 2.0 - * - * @Bean() - * @Primary() - */ -class AliyunSms implements SmsInterface -{ - /** - * @param string $content - * - * @return bool - */ - public function send(string $content): bool - { - return true; - } -} - -/** - * Class QcloudSms - * - * @since 2.0 - * - * @Bean() - */ -class QcloudSms implements SmsInterface -{ - /** - * @param string $content - * - * @return bool - */ - public function send(string $content): bool - { - return true; - } -} - -/** - * Class Sms - * - * @since 2.0 - * - * @Bean() - */ -class Sms implements SmsInterface -{ - /** - * @Inject() - * - * @var SmsInterface - */ - private $smsInterface; - - /** - * @param string $content - * - * @return bool - */ - public function send(string $content): bool - { - return $this->smsInterface->send($content); - } -} - -/* @var SmsInterface $sms*/ -$sms = BeanFactory::getBean(Sms::class); -$sms->send('sms content'); \ No newline at end of file +$el = new ExpressionLanguage(); +var_dump($el->evaluate( + "name~':'~(uid+bid)", + [ + 'name' => 'swoft', + 'uid' => 12, + 'bid' => 11 + ] +)); \ No newline at end of file From dca0ce152253b73c453e450669b32ef42b0a25d4 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+successago@users.noreply.github.com> Date: Fri, 5 Jul 2019 22:24:35 +0800 Subject: [PATCH 426/643] Update composer mirror According to https://learnku.com/articles/30758, so we would better to update. --- composer.cn.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.cn.json b/composer.cn.json index 1b6f3bb3..97a8c40e 100644 --- a/composer.cn.json +++ b/composer.cn.json @@ -54,7 +54,7 @@ "repositories": { "packagist": { "type": "composer", - "url": "https://packagist.laravel-china.org/" + "url": "https://mirrors.aliyun.com/composer/" } } } From f520c4f267d4a89f0334bbf8a4801509a70b4cd7 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sat, 6 Jul 2019 10:13:38 +0800 Subject: [PATCH 427/643] Add limiter deemo --- app/Console/Command/TestCommand.php | 5 ++ .../Handler/HttpExceptionHandler.php | 4 ++ app/Http/Controller/LimiterController.php | 72 +++++++++++++++++++ app/Model/Logic/LimiterLogic.php | 51 +++++++++++++ app/Model/Logic/RequestBean.php | 9 +++ 5 files changed, 141 insertions(+) create mode 100644 app/Http/Controller/LimiterController.php create mode 100644 app/Model/Logic/LimiterLogic.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index b73096f4..401994b2 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -117,6 +117,11 @@ private function uris(): array '/breaker/unbreak', '/breaker/breaked', '/breaker/loopBreaker' + ], + 'limiter' => [ + '/limiter/requestLimiter', + '/limiter/requestLimiter2', + '/limiter/paramLimiter?id=12', ] ]; } diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index ff272f82..19571b3f 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -10,6 +10,7 @@ use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Http\Message\Response; use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; +use Swoft\Log\Helper\CLog; use Throwable; /** @@ -29,6 +30,9 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler */ public function handle(Throwable $e, Response $response): Response { + // Log + CLog::error($e->getMessage()); + // Debug is false if (!APP_DEBUG) { return $response->withStatus(500)->withContent( diff --git a/app/Http/Controller/LimiterController.php b/app/Http/Controller/LimiterController.php new file mode 100644 index 00000000..615bf987 --- /dev/null +++ b/app/Http/Controller/LimiterController.php @@ -0,0 +1,72 @@ +getUriPath(); + return ['requestLimiter', $uri]; + } + + /** + * @RequestMapping() + * @RateLimiter(rate=20, fallback="limiterFallback") + * + * @param Request $request + * + * @return array + */ + public function requestLimiter2(Request $request): array + { + $uri = $request->getUriPath(); + return ['requestLimiter2', $uri]; + } + + /** + * @RequestMapping() + * @RateLimiter(key="request.getUriPath()~':'~request.query('id')") + * + * @param Request $request + * + * @return array + */ + public function paramLimiter(Request $request): array + { + $id = $request->query('id'); + return ['paramLimiter', $id]; + } + + /** + * @param Request $request + * + * @return array + */ + public function limiterFallback(Request $request): array + { + $uri = $request->getUriPath(); + return ['limiterFallback', $uri]; + } +} \ No newline at end of file diff --git a/app/Model/Logic/LimiterLogic.php b/app/Model/Logic/LimiterLogic.php new file mode 100644 index 00000000..90cec5fd --- /dev/null +++ b/app/Model/Logic/LimiterLogic.php @@ -0,0 +1,51 @@ + Date: Sun, 7 Jul 2019 09:26:26 +0800 Subject: [PATCH 428/643] Add task null demo --- app/Console/Command/TestCommand.php | 2 ++ app/Http/Controller/TaskController.php | 24 ++++++++++++++++++++++++ app/Task/Task/TestTask.php | 22 ++++++++++++++++++++++ app/bean.php | 2 +- bin/test.php | 18 ++++-------------- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 401994b2..522a1e01 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -100,6 +100,8 @@ private function uris(): array '/task/deleteByCo', '/task/getListByAsync', '/task/deleteByAsync', + '/task/returnNull', + '/task/returnVoid', ], 'rpc' => [ '/rpc/getList', diff --git a/app/Http/Controller/TaskController.php b/app/Http/Controller/TaskController.php index b3f4f17f..e3015f9d 100644 --- a/app/Http/Controller/TaskController.php +++ b/app/Http/Controller/TaskController.php @@ -71,4 +71,28 @@ public function deleteByAsync(): array return [$data]; } + + /** + * @RequestMapping() + * + * @return array + * @throws TaskException + */ + public function returnNull(): array + { + $result = Task::co('testTask', 'returnNull', ['name']); + return [$result]; + } + + /** + * @RequestMapping() + * + * @return array + * @throws TaskException + */ + public function returnVoid(): array + { + $result = Task::co('testTask', 'returnVoid', ['name']); + return [$result]; + } } \ No newline at end of file diff --git a/app/Task/Task/TestTask.php b/app/Task/Task/TestTask.php index 3c28ad3c..8c791171 100644 --- a/app/Task/Task/TestTask.php +++ b/app/Task/Task/TestTask.php @@ -47,4 +47,26 @@ public function delete(int $id): bool return false; } + + /** + * @TaskMapping() + * + * @param string $name + * + * @return null + */ + public function returnNull(string $name) + { + return null; + } + + /** + * @TaskMapping() + * + * @param string $name + */ + public function returnVoid(string $name): void + { + return; + } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 579d4dfa..733f2611 100644 --- a/app/bean.php +++ b/app/bean.php @@ -32,7 +32,7 @@ ], /* @see HttpServer::$setting */ 'setting' => [ - 'task_worker_num' => 12, + 'task_worker_num' => 3, 'task_enable_coroutine' => true ] ], diff --git a/bin/test.php b/bin/test.php index e48d6ce1..a2dbc989 100644 --- a/bin/test.php +++ b/bin/test.php @@ -1,18 +1,8 @@ null +]; -$data = ['life' => 10, 'universe' => 10, 'everything' => 22]; - - -$el = new ExpressionLanguage(); -var_dump($el->evaluate( - "name~':'~(uid+bid)", - [ - 'name' => 'swoft', - 'uid' => 12, - 'bid' => 11 - ] -)); \ No newline at end of file +var_dump(isset($data['result'])); \ No newline at end of file From 1b323ec9bad751b05db9304217cc484e830089e1 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 7 Jul 2019 15:39:07 +0800 Subject: [PATCH 429/643] Add rpc return null demo --- app/Console/Command/TestCommand.php | 3 ++- app/Http/Controller/RpcController.php | 13 +++++++++++++ app/Rpc/Lib/UserInterface.php | 5 +++++ app/Rpc/Service/UserService.php | 8 ++++++++ app/Rpc/Service/UserServiceV2.php | 8 ++++++++ 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 522a1e01..a3e52a5f 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -107,7 +107,8 @@ private function uris(): array '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', - '/rpc/sendBigString' + '/rpc/sendBigString', + '/rpc/returnNull' ], 'co' => [ '/co/multi' diff --git a/app/Http/Controller/RpcController.php b/app/Http/Controller/RpcController.php index 4c5546f5..18cd0e04 100644 --- a/app/Http/Controller/RpcController.php +++ b/app/Http/Controller/RpcController.php @@ -6,6 +6,7 @@ use App\Rpc\Lib\UserInterface; use Exception; use Swoft\Co; +use Swoft\Exception\SwoftException; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Rpc\Client\Annotation\Mapping\Reference; @@ -78,6 +79,7 @@ public function bigString(): array * @RequestMapping() * * @return array + * @throws SwoftException */ public function sendBigString(): array { @@ -88,6 +90,17 @@ public function sendBigString(): array return [$len, $result]; } + /** + * @RequestMapping() + * + * @return array + */ + public function returnNull(): array + { + $this->userService->returnNull(); + return [null]; + } + /** * @RequestMapping() * diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index 0b105ba2..9395518e 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -31,6 +31,11 @@ public function delete(int $id): bool; */ public function getBigContent(): string; + /** + * @return void + */ + public function returnNull():void ; + /** * Exception */ diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index 837dc3a2..cd30e12c 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -40,6 +40,14 @@ public function delete(int $id): bool return false; } + /** + * @return void + */ + public function returnNull(): void + { + return; + } + /** * @return string */ diff --git a/app/Rpc/Service/UserServiceV2.php b/app/Rpc/Service/UserServiceV2.php index 0e318e68..37c86309 100644 --- a/app/Rpc/Service/UserServiceV2.php +++ b/app/Rpc/Service/UserServiceV2.php @@ -33,6 +33,14 @@ public function getList(int $id, $type, int $count = 10): array ]; } + /** + * @return void + */ + public function returnNull(): void + { + return; + } + /** * @param int $id * From 83e83a5f52c0c93ab6f539c9386d205d7d03eaf9 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sun, 7 Jul 2019 22:15:15 +0800 Subject: [PATCH 430/643] rename migrations --- .../{AddMsg20190630164222.php => AddMsg.php} | 8 ++++---- .../{AddUser20190627225524.php => AddUser.php} | 12 ++++++------ .../{Message20190627225525.php => Message.php} | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) rename app/Migration/{AddMsg20190630164222.php => AddMsg.php} (88%) rename app/Migration/{AddUser20190627225524.php => AddUser.php} (80%) rename app/Migration/{Message20190627225525.php => Message.php} (86%) diff --git a/app/Migration/AddMsg20190630164222.php b/app/Migration/AddMsg.php similarity index 88% rename from app/Migration/AddMsg20190630164222.php rename to app/Migration/AddMsg.php index 772a2ad4..77310299 100644 --- a/app/Migration/AddMsg20190630164222.php +++ b/app/Migration/AddMsg.php @@ -12,14 +12,14 @@ * * @since 2.0 * - * @Migration() + * @Migration(20190630164222) */ -class AddMsg20190630164222 extends BaseMigration +class AddMsg extends BaseMigration { /** * @return void */ - public function up() + public function up(): void { $sql = <<increments('id'); @@ -49,10 +49,10 @@ public function up() * @throws ContainerException * @throws DbException */ - public function down() + public function down(): void { Schema::dropIfExists('users'); - Schema::getSchemaBuilder('db.pool')->dropIfExists('user'); + Schema::getSchemaBuilder('db.pool')->dropIfExists('users'); } } diff --git a/app/Migration/Message20190627225525.php b/app/Migration/Message.php similarity index 86% rename from app/Migration/Message20190627225525.php rename to app/Migration/Message.php index 44904882..6f8e3a5e 100644 --- a/app/Migration/Message20190627225525.php +++ b/app/Migration/Message.php @@ -16,9 +16,9 @@ * * @since 2.0 * - * @Migration("db2.pool") + * @Migration(time=20190627225525, pool="db2.pool") */ -class Message20190627225525 extends BaseMigration +class Message extends BaseMigration { /** * @return void @@ -27,7 +27,7 @@ class Message20190627225525 extends BaseMigration * @throws ContainerException * @throws DbException */ - public function up() + public function up(): void { $this->schema->createIfNotExists('messages', function (Blueprint $blueprint) { $blueprint->increments('id'); @@ -43,7 +43,7 @@ public function up() * @throws ContainerException * @throws DbException */ - public function down() + public function down(): void { $this->schema->dropIfExists('messages'); } From db88b3d11afe5782a122314f40ddfc7b1cc95d2e Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Mon, 8 Jul 2019 22:41:26 +0800 Subject: [PATCH 431/643] add demo --- app/Http/Controller/DbModelController.php | 5 +++ app/Listener/ModelSavedListener.php | 36 +++++++++++++++ app/Listener/RanListener.php | 33 ++++++++++++++ app/Listener/UserSavingListener.php | 35 +++++++++++++++ app/Migration/AddMsg.php | 4 -- app/Migration/Message.php | 2 +- app/Model/Entity/Count.php | 43 +++++++++++++++--- app/Model/Entity/User.php | 54 +++++++++++++---------- 8 files changed, 179 insertions(+), 33 deletions(-) create mode 100644 app/Listener/ModelSavedListener.php create mode 100644 app/Listener/RanListener.php create mode 100644 app/Listener/UserSavingListener.php diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 42620bf0..0728feb6 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -3,6 +3,7 @@ namespace App\Http\Controller; +use App\Model\Entity\Count; use App\Model\Entity\User; use Exception; use Swoft\Http\Message\Response; @@ -51,6 +52,10 @@ public function save(): array $user->save(); + $count = Count::new(); + $count->setUserId($user->getId()); + $count->save(); + return $user->toArray(); } diff --git a/app/Listener/ModelSavedListener.php b/app/Listener/ModelSavedListener.php new file mode 100644 index 00000000..0741ff82 --- /dev/null +++ b/app/Listener/ModelSavedListener.php @@ -0,0 +1,36 @@ +getTarget(); + + if ($modelStatic instanceof User) { + // to do something.... + } + + // .... + } +} diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php new file mode 100644 index 00000000..47560696 --- /dev/null +++ b/app/Listener/RanListener.php @@ -0,0 +1,33 @@ +getTarget(); + + $querySql = $event->getParam(0); + $bindings = $event->getParam(1); + } +} diff --git a/app/Listener/UserSavingListener.php b/app/Listener/UserSavingListener.php new file mode 100644 index 00000000..23c7957f --- /dev/null +++ b/app/Listener/UserSavingListener.php @@ -0,0 +1,35 @@ +getTarget(); + + if ($user->getAge() > 100) { + // stopping saving + $event->stopPropagation(true); + + $user->setAdd(100); + } + } +} diff --git a/app/Migration/AddMsg.php b/app/Migration/AddMsg.php index 77310299..3fd8b10d 100644 --- a/app/Migration/AddMsg.php +++ b/app/Migration/AddMsg.php @@ -39,10 +39,6 @@ public function up(): void */ public function down(): void { - $truncate = <<execute($truncate); $dropSql = <<attributes = $attributes; } + /** + * @param string|null $updateTime + * + * @return void + */ + public function setUpdateTime(?string $updateTime): void + { + $this->updateTime = $updateTime; + } + /** * @return int|null */ @@ -120,4 +145,12 @@ public function getAttributes(): ?string return $this->attributes; } + /** + * @return string|null + */ + public function getUpdateTime(): ?string + { + return $this->updateTime; + } + } diff --git a/app/Model/Entity/User.php b/app/Model/Entity/User.php index b465fc24..abc07444 100644 --- a/app/Model/Entity/User.php +++ b/app/Model/Entity/User.php @@ -27,6 +27,14 @@ class User extends Model */ private $id; + /** + * + * + * @Column() + * @var string + */ + private $name; + /** * * @@ -75,17 +83,10 @@ class User extends Model */ private $testJson; - /** - * - * - * @Column() - * @var string - */ - private $name; - /** * @param int|null $id + * * @return void */ public function setId(?int $id): void @@ -93,8 +94,19 @@ public function setId(?int $id): void $this->id = $id; } + /** + * @param string $name + * + * @return void + */ + public function setName(string $name): void + { + $this->name = $name; + } + /** * @param int $age + * * @return void */ public function setAge(int $age): void @@ -104,6 +116,7 @@ public function setAge(int $age): void /** * @param string $password + * * @return void */ public function setPassword(string $password): void @@ -113,6 +126,7 @@ public function setPassword(string $password): void /** * @param string $userDesc + * * @return void */ public function setUserDesc(string $userDesc): void @@ -122,6 +136,7 @@ public function setUserDesc(string $userDesc): void /** * @param int|null $add + * * @return void */ public function setAdd(?int $add): void @@ -131,6 +146,7 @@ public function setAdd(?int $add): void /** * @param int|null $hahh + * * @return void */ public function setHahh(?int $hahh): void @@ -140,6 +156,7 @@ public function setHahh(?int $hahh): void /** * @param array|null $testJson + * * @return void */ public function setTestJson(?array $testJson): void @@ -148,20 +165,19 @@ public function setTestJson(?array $testJson): void } /** - * @param string $name - * @return void + * @return int|null */ - public function setName(string $name): void + public function getId(): ?int { - $this->name = $name; + return $this->id; } /** - * @return int|null + * @return string */ - public function getId(): ?int + public function getName(): string { - return $this->id; + return $this->name; } /** @@ -212,12 +228,4 @@ public function getTestJson(): ?array return $this->testJson; } - /** - * @return string - */ - public function getName(): string - { - return $this->name; - } - } From 30a22aa81573c68b1f667536e5e88263d6baf8f5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 8 Jul 2019 22:55:44 +0800 Subject: [PATCH 432/643] Modify format --- app/Http/Controller/ValidatorController.php | 4 ++-- bin/test.php | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) delete mode 100644 bin/test.php diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php index 8f339209..213e3143 100644 --- a/app/Http/Controller/ValidatorController.php +++ b/app/Http/Controller/ValidatorController.php @@ -33,7 +33,7 @@ function validateAll(Request $request): array * Verify only the type field in the TestValidator validator * * @RequestMapping() - * @Validate(validator="TestValidator",fields={"type"}) + * @Validate(validator="TestValidator", fields={"type"}) * * @param Request $request * @@ -48,7 +48,7 @@ function validateType(Request $request): array * Verify only the password field in the TestValidator validator * * @RequestMapping() - * @Validate(validator="TestValidator",fields={"password"}) + * @Validate(validator="TestValidator", fields={"password"}) * * @param Request $request * diff --git a/bin/test.php b/bin/test.php deleted file mode 100644 index a2dbc989..00000000 --- a/bin/test.php +++ /dev/null @@ -1,8 +0,0 @@ - null -]; - -var_dump(isset($data['result'])); \ No newline at end of file From 870561277dd748e3684f2557876f0ec63933776a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 9 Jul 2019 00:45:51 +0800 Subject: [PATCH 433/643] Add composer --- app/bean.php | 13 +++---------- composer.json | 8 +++++++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/bean.php b/app/bean.php index 733f2611..0a184a8b 100644 --- a/app/bean.php +++ b/app/bean.php @@ -45,13 +45,13 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.3', + 'dsn' => 'mysql:dbname=test;host=192.168.4.11', 'username' => 'root', 'password' => 'swoft123456', ], 'db2' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', + 'dsn' => 'mysql:dbname=test2;host=192.168.4.11', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) @@ -62,7 +62,7 @@ ], 'db3' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=172.17.0.3', + 'dsn' => 'mysql:dbname=test2;host=192.168.4.11', 'username' => 'root', 'password' => 'swoft123456' ], @@ -112,12 +112,5 @@ ], 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], - ], - 'apollo' => [ - 'host' => '192.168.4.11', - 'timeout' => -1 - ], - 'consul' => [ - 'host' => '192.168.4.11' ] ]; diff --git a/composer.json b/composer.json index 96edb8ff..d7092dcf 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,13 @@ "swoft/http-server": "~2.0.0", "swoft/rpc-client": "~2.0.0", "swoft/rpc-server": "~2.0.0", - "swoft/websocket-server": "~2.0.0" + "swoft/websocket-server": "~2.0.0", + "swoft/tcp": "~2.0.0", + "swoft/tcp-server": "~2.0.0", + "swoft/apollo": "~2.0.0", + "swoft/consul": "~2.0.0", + "swoft/limiter": "~2.0.0", + "swoft/breaker": "~2.0.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", From 32da871800f55af6b49696121f0f27bb8a6f2361 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 9 Jul 2019 11:54:26 +0800 Subject: [PATCH 434/643] Update Dockerfile --- Dockerfile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5362bfe4..7a675f70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,14 +31,17 @@ ADD . /var/www/swoft # Timezone RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone \ -# Libs +# Libs -y --no-install-recommends && apt-get update \ - && apt-get install -y --no-install-recommends \ + && apt-get install \ curl wget git zip unzip less vim openssl \ libz-dev \ libssl-dev \ libnghttp2-dev \ libpcre3-dev \ + libjpeg-dev \ + libpng-dev \ + libfreetype6-dev \ # Install composer && curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ @@ -74,7 +77,7 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "[Date]\ndate.timezone=${TIMEZONE}" > /usr/local/etc/php/conf.d/timezone.ini \ # Install composer deps && cd /var/www/swoft \ - && composer install --no-dev \ + && composer install \ && composer clearcache WORKDIR /var/www/swoft From 859fc73b7a4e6e8107c6a373f85750125e4ae0da Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 9 Jul 2019 12:01:37 +0800 Subject: [PATCH 435/643] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 7a675f70..496c5e22 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone \ # Libs -y --no-install-recommends && apt-get update \ - && apt-get install \ + && apt-get install -y \ curl wget git zip unzip less vim openssl \ libz-dev \ libssl-dev \ From 3ab365956cbbac1048a981066688550df2bc1e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 9 Jul 2019 12:41:58 +0800 Subject: [PATCH 436/643] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fd131c0b..ac907bb7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,9 @@ ![](public/image/start-http-server.jpg) -⚡️ Modern High performance AOP and Coroutine PHP Framework, base on Swoole +Swoft is a PHP microservices coroutine framework based on the Swoole extension. Like Go, Swoft has a built-in coroutine web server and a common coroutine client and is resident in memory, independent of traditional PHP-FPM. There are similar Go language operations, similar to the Spring Cloud framework flexible annotations, powerful global dependency injection container, comprehensive service governance, flexible and powerful AOP, standard PSR specification implementation and so on. + +Through three years of accumulation and direction exploration, Swoft has made Swoft the Spring Cloud in the PHP world, which is the best choice for PHP's high-performance framework and microservices management. > **[中文说明](README.zh-CN.md)** From d7ab215ed8100bafbdfd7c560fd2a8f6248b52e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 9 Jul 2019 12:47:45 +0800 Subject: [PATCH 437/643] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ac907bb7..a3107544 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ ![](public/image/start-http-server.jpg) +PHP microservices coroutine framework + +## Introduction + Swoft is a PHP microservices coroutine framework based on the Swoole extension. Like Go, Swoft has a built-in coroutine web server and a common coroutine client and is resident in memory, independent of traditional PHP-FPM. There are similar Go language operations, similar to the Spring Cloud framework flexible annotations, powerful global dependency injection container, comprehensive service governance, flexible and powerful AOP, standard PSR specification implementation and so on. Through three years of accumulation and direction exploration, Swoft has made Swoft the Spring Cloud in the PHP world, which is the best choice for PHP's high-performance framework and microservices management. From 41678601704d30f1088f79d9d7ea514c7139344d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 9 Jul 2019 12:50:40 +0800 Subject: [PATCH 438/643] Update README.zh-CN.md --- README.zh-CN.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index bc55cb1a..d7f40038 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -14,10 +14,16 @@ ![](public/image/start-http-server.jpg) -⚡️ 现代的高性能的 AOP & Coroutine PHP框架 +PHP 高性能微服务协程框架 > **[EN README](README.md)** +## 简介 + +Swoft 是一款基于 Swoole 扩展实现的 PHP 微服务协程框架。Swoft 能像 Go 一样,内置协程网络服务器及常用的协程客户端且常驻内存,不依赖传统的 PHP-FPM。有类似 Go 语言的协程操作方式,有类似 Spring Cloud 框架灵活的注解、强大的全局依赖注入容器、完善的服务治理、灵活强大的 AOP、标准的 PSR 规范实现等等。 + +Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP 界的 Spring Cloud, 它是 PHP 高性能框架和微服务治理的最佳选择。 + ## 功能特色 - 内置高性能网络服务器(Http/Websocket/RPC) From 7ffcf70b323e23a85673dc32083a736e8128923d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 9 Jul 2019 12:52:24 +0800 Subject: [PATCH 439/643] Update README.zh-CN.md --- README.zh-CN.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.zh-CN.md b/README.zh-CN.md index d7f40038..129c2f79 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -44,6 +44,13 @@ Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP - 高效的任务处理 - 灵活的异常处理 - 强大的日志系统 + - 服务注册与发现 + - 配置中心 + - 服务限流 + - 服务降级 + - 服务熔断 + - Apollo + - Consul ## 在线文档 From 98c1bdc4c680048870cfb09cc213b676eaa498e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 9 Jul 2019 12:56:16 +0800 Subject: [PATCH 440/643] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a3107544..42fb063b 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,12 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Efficient task processing - Flexible exception handling - Powerful log system +- Service registration & discovery +- Service breaker +- Service restrictions +- Service fallback +- Apollo +- Consul ## Document From eae9759d7a22c2d7304c0a688c61646e87a4499f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 9 Jul 2019 12:56:46 +0800 Subject: [PATCH 441/643] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 42fb063b..b3edb24d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Service breaker - Service restrictions - Service fallback +- Configuration Center - Apollo - Consul From 487befc9c1db4bd70fea8759d10f57ee80f130f7 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 9 Jul 2019 14:31:07 +0800 Subject: [PATCH 442/643] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b3edb24d..e809f439 100644 --- a/README.md +++ b/README.md @@ -11,19 +11,20 @@ [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +[![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) ![](public/image/start-http-server.jpg) PHP microservices coroutine framework +> **[中文说明](README.zh-CN.md)** + ## Introduction Swoft is a PHP microservices coroutine framework based on the Swoole extension. Like Go, Swoft has a built-in coroutine web server and a common coroutine client and is resident in memory, independent of traditional PHP-FPM. There are similar Go language operations, similar to the Spring Cloud framework flexible annotations, powerful global dependency injection container, comprehensive service governance, flexible and powerful AOP, standard PSR specification implementation and so on. Through three years of accumulation and direction exploration, Swoft has made Swoft the Spring Cloud in the PHP world, which is the best choice for PHP's high-performance framework and microservices management. -> **[中文说明](README.zh-CN.md)** - ## Feature - Built-in high performance network server(Http/Websocket/RPC) From 92ddde031317e95bb43375d8353fa04ec4791319 Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 9 Jul 2019 17:10:31 +0800 Subject: [PATCH 443/643] add some tcp classes --- app/Tcp/Controller/DemoController.php | 30 +++++++++++++++++++++++++++ dev.composer.json | 12 +++++++---- 2 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 app/Tcp/Controller/DemoController.php diff --git a/app/Tcp/Controller/DemoController.php b/app/Tcp/Controller/DemoController.php new file mode 100644 index 00000000..3c8734a8 --- /dev/null +++ b/app/Tcp/Controller/DemoController.php @@ -0,0 +1,30 @@ + Date: Wed, 10 Jul 2019 12:16:01 +0800 Subject: [PATCH 444/643] Update docker-compose.yml --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5f216e6f..49003d59 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: - "18307:18307" - "18308:18308" volumes: - - ./:/var/www + - ./:/var/www/swoft # - ./tmp/ng-conf:/etc/nginx # - ./tmp/logs:/var/log From 219afcf0cf1dff474aa5de4ea911bf280386006a Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 10 Jul 2019 12:40:17 +0800 Subject: [PATCH 445/643] update some info, fix docker compose --- .dockerignore | 1 + README.md | 4 ++-- README.zh-CN.md | 5 +++-- app/Application.php | 2 -- bin/swoft | 4 +--- composer.json | 7 +++---- docker-compose.yml | 4 ++-- 7 files changed, 12 insertions(+), 15 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..e4d05d91 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +runtime diff --git a/README.md b/README.md index e809f439..4eb911d5 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@ [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) +[![Docker Build Status](https://img.shields.io/docker/build/swoft/swoft.svg)](https://hub.docker.com/r/swoft/swoft/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) [![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) -![](public/image/start-http-server.jpg) +![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/start-http-server.jpg) PHP microservices coroutine framework diff --git a/README.zh-CN.md b/README.zh-CN.md index 129c2f79..b174d3e6 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -6,13 +6,14 @@ [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/alphp/) +[![Docker Build Status](https://img.shields.io/docker/build/swoft/swoft.svg)](https://hub.docker.com/r/swoft/swoft/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) +[![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) -![](public/image/start-http-server.jpg) +![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/start-http-server.jpg) PHP 高性能微服务协程框架 diff --git a/app/Application.php b/app/Application.php index d68e92c6..c7003af9 100644 --- a/app/Application.php +++ b/app/Application.php @@ -1,9 +1,7 @@ 300000, ]); // Run application -(new \App\Application())->run(); \ No newline at end of file +(new \App\Application())->run(); diff --git a/composer.json b/composer.json index d7092dcf..1d0aec11 100644 --- a/composer.json +++ b/composer.json @@ -24,17 +24,16 @@ "swoft/rpc-client": "~2.0.0", "swoft/rpc-server": "~2.0.0", "swoft/websocket-server": "~2.0.0", - "swoft/tcp": "~2.0.0", "swoft/tcp-server": "~2.0.0", "swoft/apollo": "~2.0.0", "swoft/consul": "~2.0.0", "swoft/limiter": "~2.0.0", - "swoft/breaker": "~2.0.0" + "swoft/breaker": "~2.0.0", + "swoft/devtool": "~2.0.0" }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5", - "swoft/devtool": "~2.0.0" + "phpunit/phpunit": "^7.5" }, "autoload": { "psr-4": { diff --git a/docker-compose.yml b/docker-compose.yml index 5f216e6f..05aa4ff1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: - "18307:18307" - "18308:18308" volumes: - - ./:/var/www + - ./:/var/www/swoft # - ./tmp/ng-conf:/etc/nginx # - ./tmp/logs:/var/log @@ -27,7 +27,7 @@ services: ports: - "13306:3306" volumes: - - ./tmp/data/mysql:/var/lib/mysql + - ./runtime/data/mysql:/var/lib/mysql restart: always redis: From 743155bae77a1163263eadf12352601fcd6baa7d Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 12 Jul 2019 11:14:54 +0800 Subject: [PATCH 446/643] Fixed for swoole 4.4 --- app/Listener/DeregisterServiceListener.php | 17 ++++++++++++----- app/Listener/RegisterServiceListener.php | 13 ++++++------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/Listener/DeregisterServiceListener.php b/app/Listener/DeregisterServiceListener.php index bae52825..78ace1ae 100644 --- a/app/Listener/DeregisterServiceListener.php +++ b/app/Listener/DeregisterServiceListener.php @@ -4,13 +4,19 @@ namespace App\Listener; +use ReflectionException; use Swoft\Bean\Annotation\Mapping\Inject; +use Swoft\Bean\Exception\ContainerException; +use Swoft\Co; use Swoft\Consul\Agent; +use Swoft\Consul\Exception\ClientException; +use Swoft\Consul\Exception\ServerException; use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; use Swoft\Http\Server\HttpServer; use Swoft\Server\Swoole\SwooleEvent; +use Swoole\Coroutine; /** * Class DeregisterServiceListener @@ -30,16 +36,17 @@ class DeregisterServiceListener implements EventHandlerInterface /** * @param EventInterface $event + * + * @throws ReflectionException + * @throws ContainerException + * @throws ClientException + * @throws ServerException */ public function handle(EventInterface $event): void { /* @var HttpServer $httpServer */ $httpServer = $event->getTarget(); -// $scheduler = Swoole\Coroutine\Scheduler(); -// $scheduler->add(function () use ($httpServer) { -// $this->agent->deregisterService('swoft'); -// }); -// $scheduler->start(); +// $this->agent->deregisterService('swoft'); } } \ No newline at end of file diff --git a/app/Listener/RegisterServiceListener.php b/app/Listener/RegisterServiceListener.php index 101cb596..9bdce35d 100644 --- a/app/Listener/RegisterServiceListener.php +++ b/app/Listener/RegisterServiceListener.php @@ -5,6 +5,7 @@ use Swoft\Bean\Annotation\Mapping\Inject; +use Swoft\Co; use Swoft\Consul\Agent; use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; @@ -12,6 +13,7 @@ use Swoft\Http\Server\HttpServer; use Swoft\Log\Helper\CLog; use Swoft\Server\Swoole\SwooleEvent; +use Swoole\Coroutine; /** * Class RegisterServiceListener @@ -56,12 +58,9 @@ public function handle(EventInterface $event): void ]; -// $scheduler = Swoole\Coroutine\Scheduler(); -// $scheduler->add(function () use ($service) { -// // Register -// $this->agent->registerService($service); -// CLog::info('Swoft http register service success by consul!'); -// }); -// $scheduler->start(); + // Register +// $this->agent->registerService($service); +// CLog::info('Swoft http register service success by consul!'); + } } \ No newline at end of file From f4badd6fa17fe40e79abde9c068c9a90533c5aba Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 13 Jul 2019 14:34:55 +0800 Subject: [PATCH 447/643] Update Dockerfile --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 496c5e22..ef58af06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,4 +83,5 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ WORKDIR /var/www/swoft EXPOSE 18306 18307 18308 -ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "http:start"] +# ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "http:start"] +CMD ["php", "/var/www/swoft/bin/swoft", "http:start"] From e0146dbffdcffcc395b96cc5db76847bd844a836 Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 13 Jul 2019 18:32:20 +0800 Subject: [PATCH 448/643] update some info --- Dockerfile | 3 ++- app/Application.php | 6 ++++++ app/Http/Controller/LimiterController.php | 2 +- test/bootstrap.php | 2 -- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 496c5e22..c2508c09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,4 +83,5 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ WORKDIR /var/www/swoft EXPOSE 18306 18307 18308 -ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "http:start"] +#ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "http:start"] +CMD ["php", "/var/www/swoft/bin/swoft", "http:start"] diff --git a/app/Application.php b/app/Application.php index c7003af9..43f71606 100644 --- a/app/Application.php +++ b/app/Application.php @@ -3,6 +3,7 @@ namespace App; use Swoft\SwoftApplication; +use function date_default_timezone_set; /** * Class Application @@ -11,5 +12,10 @@ */ class Application extends SwoftApplication { + protected function beforeInit(): void + { + parent::beforeInit(); + date_default_timezone_set('Asia/Shanghai'); + } } diff --git a/app/Http/Controller/LimiterController.php b/app/Http/Controller/LimiterController.php index 615bf987..6796e168 100644 --- a/app/Http/Controller/LimiterController.php +++ b/app/Http/Controller/LimiterController.php @@ -69,4 +69,4 @@ public function limiterFallback(Request $request): array $uri = $request->getUriPath(); return ['limiterFallback', $uri]; } -} \ No newline at end of file +} diff --git a/test/bootstrap.php b/test/bootstrap.php index 7cd4eefb..a4abe2da 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,4 +1,2 @@ Date: Sat, 13 Jul 2019 23:39:53 +0800 Subject: [PATCH 449/643] update some bean config --- app/Console/Command/TestCommand.php | 56 ++--------------------------- app/bean.php | 9 ++++- 2 files changed, 11 insertions(+), 54 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index a3e52a5f..24da8bca 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -1,20 +1,15 @@ getSameOpt(['host', 'H'], '127.0.0.1'); - $port = $input->getSameOpt(['port', 'p'], 18309); - - if (!$ok = $cli->connect((string)$host, (int)$port, 5.0)) { - $code = $cli->errCode; - $msg = socket_strerror($code); - Show::error("Connect failed. Error($code): $msg"); - return; - } - - $addr = $host . ':' . $port; - $output->colored('Successful connect to tcp server ' . $addr, 'success'); - - while (true) { - if (!$msg = $input->read('> ')) { - $output->liteWarning('Please input message for send'); - continue; - } - - // Exit interactive terminal - if ($msg === 'quit' || $msg === 'exit') { - $output->colored('Quit, Bye!'); - break; - } - - $cli->send($msg); - - $res = $cli->recv(); - $output->writef('Return: %s', $res); - } - - $cli->close(); - } } diff --git a/app/bean.php b/app/bean.php index 0a184a8b..617b24f0 100644 --- a/app/bean.php +++ b/app/bean.php @@ -10,7 +10,7 @@ use Swoft\Rpc\Server\ServiceServer; use Swoft\Http\Server\Swoole\RequestListener; use Swoft\WebSocket\Server\WebSocketServer; -use Swoft\Server\Swoole\SwooleEvent; +use Swoft\Server\SwooleEvent; use Swoft\Db\Database; use Swoft\Redis\RedisDb; @@ -100,6 +100,7 @@ ], 'wsServer' => [ 'class' => WebSocketServer::class, + 'port' => 18308, 'on' => [ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), @@ -110,6 +111,12 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], + 'tcpServer' => [ + 'port' => 18309, + ], + 'tcpServerProtocol' => [ + 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + ], 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], ] From 711f798019fb1bf03252877a9fce651e53bb1ae5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Sun, 14 Jul 2019 17:15:24 +0800 Subject: [PATCH 450/643] Add user process --- app/Listener/UserSavingListener.php | 18 +++++------ app/Model/Logic/MonitorLogic.php | 47 +++++++++++++++++++++++++++++ app/Process/MonitorProcess.php | 39 ++++++++++++++++++++++++ app/bean.php | 12 +++++--- 4 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 app/Model/Logic/MonitorLogic.php create mode 100644 app/Process/MonitorProcess.php diff --git a/app/Listener/UserSavingListener.php b/app/Listener/UserSavingListener.php index 23c7957f..263bd099 100644 --- a/app/Listener/UserSavingListener.php +++ b/app/Listener/UserSavingListener.php @@ -22,14 +22,14 @@ class UserSavingListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - /* @var User $user */ - $user = $event->getTarget(); - - if ($user->getAge() > 100) { - // stopping saving - $event->stopPropagation(true); - - $user->setAdd(100); - } +// /* @var User $user */ +// $user = $event->getTarget(); +// +// if ($user->getAge() > 100) { +// // stopping saving +// $event->stopPropagation(true); +// +// $user->setAdd(100); +// } } } diff --git a/app/Model/Logic/MonitorLogic.php b/app/Model/Logic/MonitorLogic.php new file mode 100644 index 00000000..23bfa5c8 --- /dev/null +++ b/app/Model/Logic/MonitorLogic.php @@ -0,0 +1,47 @@ +name('swoft-monitor'); + + while (true) { + $connections = context()->getServer()->getSwooleServer()->connections; + CLog::info('monitor = ' . json_encode($connections)); + + // Database + $user = User::find(1)->toArray(); + CLog::info('user='.json_encode($user)); + + // Redis + Redis::set('test', 'ok'); + CLog::info('test='.Redis::get('test')); + + Coroutine::sleep(3); + } + } +} \ No newline at end of file diff --git a/app/Process/MonitorProcess.php b/app/Process/MonitorProcess.php new file mode 100644 index 00000000..d9b1650a --- /dev/null +++ b/app/Process/MonitorProcess.php @@ -0,0 +1,39 @@ +logic->monitor($process); + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 0a184a8b..f38b6325 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,6 +1,7 @@ [ 'flushRequest' => true, - 'enable' => false, + 'enable' => true, 'json' => false, ], 'httpServer' => [ @@ -26,6 +27,9 @@ 'listener' => [ 'rpc' => bean('rpcServer') ], + 'process' => [ + 'monitor' => bean(MonitorProcess::class) + ], 'on' => [ SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event SwooleEvent::FINISH => bean(FinishListener::class) @@ -45,13 +49,13 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=192.168.4.11', + 'dsn' => 'mysql:dbname=test;host=172.17.0.2', 'username' => 'root', 'password' => 'swoft123456', ], 'db2' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=192.168.4.11', + 'dsn' => 'mysql:dbname=test2;host=172.17.0.2', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) @@ -62,7 +66,7 @@ ], 'db3' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=192.168.4.11', + 'dsn' => 'mysql:dbname=test2;host=172.17.0.2', 'username' => 'root', 'password' => 'swoft123456' ], From 0f9ce68212e1cba7c397847002c510e682e00a08 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Mon, 15 Jul 2019 20:29:41 +0800 Subject: [PATCH 451/643] Add process pool demo --- app/Process/Worker1Process.php | 34 ++++++++++++++++++++++++++++++++++ app/Process/Worker2Process.php | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 app/Process/Worker1Process.php create mode 100644 app/Process/Worker2Process.php diff --git a/app/Process/Worker1Process.php b/app/Process/Worker1Process.php new file mode 100644 index 00000000..7d4eb23e --- /dev/null +++ b/app/Process/Worker1Process.php @@ -0,0 +1,34 @@ + Date: Tue, 16 Jul 2019 14:58:04 +0800 Subject: [PATCH 452/643] Add demo --- app/Console/Command/TestCommand.php | 3 ++ app/Http/Controller/TaskController.php | 18 ++++++++++ app/Process/Worker1Process.php | 4 +-- app/Process/Worker2Process.php | 4 +-- app/Task/Task/SyncTask.php | 49 ++++++++++++++++++++++++++ app/bean.php | 10 +++--- 6 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 app/Task/Task/SyncTask.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index a3e52a5f..f730e516 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -15,6 +15,7 @@ use Swoft\Console\Output\Output; use Swoft\Http\Server\Router\Route; use Swoole\Client; +use Swoole\Coroutine; /** * Class TestCommand @@ -53,6 +54,8 @@ public function ab() output()->writeln('执行URL:' . $abShell . PHP_EOL); exec($abShell, $abResult); + + sleep(1); } } diff --git a/app/Http/Controller/TaskController.php b/app/Http/Controller/TaskController.php index e3015f9d..0168e143 100644 --- a/app/Http/Controller/TaskController.php +++ b/app/Http/Controller/TaskController.php @@ -95,4 +95,22 @@ public function returnVoid(): array $result = Task::co('testTask', 'returnVoid', ['name']); return [$result]; } + + /** + * @RequestMapping() + * + * @return array + * @throws TaskException + */ + public function syncTask(): array + { + $result = Task::co('sync', 'test', ['name']); + $result2 = Task::co('sync', 'testBool', []); + $result3 = Task::co('sync', 'testNull', []); + + $data[] = $result; + $data[] = $result2; + $data[] = $result3; + return $data; + } } \ No newline at end of file diff --git a/app/Process/Worker1Process.php b/app/Process/Worker1Process.php index 7d4eb23e..3d2e36c8 100644 --- a/app/Process/Worker1Process.php +++ b/app/Process/Worker1Process.php @@ -25,8 +25,8 @@ class Worker1Process implements ProcessInterface */ public function run(Pool $pool, int $workerId): void { - while (true){ - CLog::info('worker-1'); + while (true) { + CLog::info('worker-' . $workerId); Coroutine::sleep(3); } diff --git a/app/Process/Worker2Process.php b/app/Process/Worker2Process.php index 4904358c..54d0835a 100644 --- a/app/Process/Worker2Process.php +++ b/app/Process/Worker2Process.php @@ -25,8 +25,8 @@ class Worker2Process implements ProcessInterface */ public function run(Pool $pool, int $workerId): void { - while (true){ - CLog::info('worker-2'); + while (true) { + CLog::info('worker-' . $workerId); Coroutine::sleep(3); } diff --git a/app/Task/Task/SyncTask.php b/app/Task/Task/SyncTask.php new file mode 100644 index 00000000..3c68b5ec --- /dev/null +++ b/app/Task/Task/SyncTask.php @@ -0,0 +1,49 @@ + bean('rpcServer') ], 'process' => [ - 'monitor' => bean(MonitorProcess::class) +// 'monitor' => bean(MonitorProcess::class) ], 'on' => [ - SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event - SwooleEvent::FINISH => bean(FinishListener::class) + SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task +// SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event +// SwooleEvent::FINISH => bean(FinishListener::class) ], /* @see HttpServer::$setting */ 'setting' => [ 'task_worker_num' => 3, - 'task_enable_coroutine' => true +// 'task_enable_coroutine' => true ] ], 'httpDispatcher' => [ From b80968e914ad7b58902abde15c3959e8591e29a1 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 16 Jul 2019 15:35:28 +0800 Subject: [PATCH 453/643] Set default co task --- app/bean.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/bean.php b/app/bean.php index aeaa0bbf..5245b456 100644 --- a/app/bean.php +++ b/app/bean.php @@ -32,14 +32,14 @@ // 'monitor' => bean(MonitorProcess::class) ], 'on' => [ - SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task -// SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event -// SwooleEvent::FINISH => bean(FinishListener::class) +// SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task + SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event + SwooleEvent::FINISH => bean(FinishListener::class) ], /* @see HttpServer::$setting */ 'setting' => [ - 'task_worker_num' => 3, -// 'task_enable_coroutine' => true + 'task_worker_num' => 12, + 'task_enable_coroutine' => true ] ], 'httpDispatcher' => [ From 5239a2a5c19f779ed55fdec8c993837b716d1e9a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 16 Jul 2019 17:09:16 +0800 Subject: [PATCH 454/643] Modify demo --- app/Process/Worker1Process.php | 2 +- app/Process/Worker2Process.php | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/Process/Worker1Process.php b/app/Process/Worker1Process.php index 3d2e36c8..c802ff73 100644 --- a/app/Process/Worker1Process.php +++ b/app/Process/Worker1Process.php @@ -15,7 +15,7 @@ * * @since 2.0 * - * @Process(workerId=1) + * @Process(workerId=0) */ class Worker1Process implements ProcessInterface { diff --git a/app/Process/Worker2Process.php b/app/Process/Worker2Process.php index 54d0835a..a811174f 100644 --- a/app/Process/Worker2Process.php +++ b/app/Process/Worker2Process.php @@ -4,9 +4,12 @@ namespace App\Process; +use App\Model\Entity\User; +use Swoft\Db\Exception\DbException; use Swoft\Log\Helper\CLog; use Swoft\Process\Annotation\Mapping\Process; use Swoft\Process\Contract\ProcessInterface; +use Swoft\Redis\Redis; use Swoole\Coroutine; use Swoole\Process\Pool; @@ -15,18 +18,29 @@ * * @since 2.0 * - * @Process(workerId=2) + * @Process(workerId={1,2}) */ class Worker2Process implements ProcessInterface { /** * @param Pool $pool * @param int $workerId + * + * @throws DbException */ public function run(Pool $pool, int $workerId): void { while (true) { - CLog::info('worker-' . $workerId); + + // Database + $user = User::find(1)->toArray(); + CLog::info('user='.json_encode($user)); + + // Redis + Redis::set('test', 'ok'); + CLog::info('test='.Redis::get('test')); + + CLog::info('worker-' . $workerId.' context='.context()->getWorkerId()); Coroutine::sleep(3); } From 27c6d57771c7d81dce08ecb4dd1ee1dabaa2f8ff Mon Sep 17 00:00:00 2001 From: beckjiang Date: Wed, 17 Jul 2019 18:53:22 +0800 Subject: [PATCH 455/643] =?UTF-8?q?=E4=BC=98=E5=8C=96Dockerfile=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0gitignore=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 ++- Dockerfile | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index efc949ab..734bd182 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,10 @@ runtime/ vendor/ temp/ +tmp/ *.lock .phpintel/ .env .phpstorm.meta.php .DS_Store -public/devtool/ \ No newline at end of file +public/devtool/ diff --git a/Dockerfile b/Dockerfile index ef58af06..26037162 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,8 +26,6 @@ ENV APP_ENV=${app_env:-"prod"} \ SWOOLE_VERSION=4.3.5 \ COMPOSER_ALLOW_SUPERUSER=1 -ADD . /var/www/swoft - # Timezone RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone \ @@ -74,9 +72,11 @@ RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ # Timezone && cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ && echo "${TIMEZONE}" > /etc/timezone \ - && echo "[Date]\ndate.timezone=${TIMEZONE}" > /usr/local/etc/php/conf.d/timezone.ini \ + && echo "[Date]\ndate.timezone=${TIMEZONE}" > /usr/local/etc/php/conf.d/timezone.ini + # Install composer deps - && cd /var/www/swoft \ +ADD . /var/www/swoft +RUN cd /var/www/swoft \ && composer install \ && composer clearcache @@ -85,3 +85,4 @@ EXPOSE 18306 18307 18308 # ENTRYPOINT ["php", "/var/www/swoft/bin/swoft", "http:start"] CMD ["php", "/var/www/swoft/bin/swoft", "http:start"] + From 03bb46e8fc4816e94a66563603927d323789c3de Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 17 Jul 2019 19:33:51 +0800 Subject: [PATCH 456/643] Update docker-compose.yml --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 49003d59..0149d563 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,8 +16,8 @@ services: - "18308:18308" volumes: - ./:/var/www/swoft - # - ./tmp/ng-conf:/etc/nginx - # - ./tmp/logs:/var/log + # - ./runtime/ng-conf:/etc/nginx + # - ./runtime/logs:/var/log mysql: image: mysql @@ -27,7 +27,7 @@ services: ports: - "13306:3306" volumes: - - ./tmp/data/mysql:/var/lib/mysql + - ./runtime/data/mysql:/var/lib/mysql restart: always redis: From 22011c395ee24d5f606e2d141f41bb5373c89762 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 17 Jul 2019 19:36:07 +0800 Subject: [PATCH 457/643] Update .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 734bd182..fa561a74 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ .git/ runtime/ vendor/ -temp/ -tmp/ *.lock .phpintel/ .env From 0cd11d8b0975d23f2a49293e91244f145a230187 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Wed, 17 Jul 2019 20:37:50 +0800 Subject: [PATCH 458/643] Add lua pre --- app/bean.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/bean.php b/app/bean.php index 5245b456..5300227c 100644 --- a/app/bean.php +++ b/app/bean.php @@ -84,6 +84,9 @@ 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, + 'option' => [ + 'prefix' => 'swoft:prefix:' + ] ], 'user' => [ 'class' => ServiceClient::class, From a0e53829d49a8e55b501abc21035a54f78b9f826 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Wed, 17 Jul 2019 21:11:48 +0800 Subject: [PATCH 459/643] add sql parse --- app/Listener/RanListener.php | 51 +++++++++++++++++++++++++++-- app/Listener/UserSavingListener.php | 21 +++++++----- app/bean.php | 31 ++++++++++-------- 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index 47560696..829ce42c 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -8,6 +8,7 @@ use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; +use Swoft\Stdlib\Helper\StringHelper; /** * Class RanListener @@ -18,16 +19,62 @@ */ class RanListener implements EventHandlerInterface { + /** + * SQL ran + * * @param EventInterface $event + * */ public function handle(EventInterface $event): void { - /* @var Connection $connection */ $connection = $event->getTarget(); - $querySql = $event->getParam(0); + $querySql = $event->getParam(0); $bindings = $event->getParam(1); + + $rawSql = $this->getRawSql($querySql, $bindings, $connection); + + output()->info($rawSql); + } + + /** + * Returns the raw SQL by inserting parameter values into the corresponding placeholders in [[sql]]. + * Note that the return value of this method should mainly be used for logging purpose. + * It is likely that this method returns an invalid SQL due to improper replacement of parameter placeholders. + * + * @param string $sql + * @param array $bindings + * @param Connection $connection + * + * @return string the raw SQL with parameter values inserted into the corresponding placeholders in [[sql]]. + */ + public function getRawSql(string $sql, array $bindings, Connection $connection) + { + if (empty($bindings)) { + return $sql; + } + foreach ($bindings as $name => $value) { + if (is_string($name) && strncmp(':', $name, 1)) { + $name = ':' . $name; + } else { + $name = '?'; + } + + if (is_string($value) || is_array($value)) { + $param = $connection->getQueryGrammar()->quoteString($value); + } elseif (is_bool($value)) { + $param = ($value ? 'TRUE' : 'FALSE'); + } elseif ($value === null) { + $param = 'NULL'; + } else { + $param = (string)$value; + } + + $sql = StringHelper::replaceFirst($name, $param, $sql); + } + + return $sql; } } diff --git a/app/Listener/UserSavingListener.php b/app/Listener/UserSavingListener.php index 263bd099..cf278e4f 100644 --- a/app/Listener/UserSavingListener.php +++ b/app/Listener/UserSavingListener.php @@ -22,14 +22,17 @@ class UserSavingListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { -// /* @var User $user */ -// $user = $event->getTarget(); -// -// if ($user->getAge() > 100) { -// // stopping saving -// $event->stopPropagation(true); -// -// $user->setAdd(100); -// } + + /* @var User $user */ + $user = $event->getTarget(); + + /** + if ($user->getAge() > 100) { + // stopping saving + $event->stopPropagation(true); + + $user->setAdd(100); + } + */ } } diff --git a/app/bean.php b/app/bean.php index 5245b456..287ab3fa 100644 --- a/app/bean.php +++ b/app/bean.php @@ -17,12 +17,12 @@ use Swoft\Redis\RedisDb; return [ - 'logger' => [ + 'logger' => [ 'flushRequest' => true, 'enable' => true, 'json' => false, ], - 'httpServer' => [ + 'httpServer' => [ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ @@ -42,50 +42,53 @@ 'task_enable_coroutine' => true ] ], - 'httpDispatcher' => [ + 'httpDispatcher' => [ // Add global http middleware 'middlewares' => [ // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], ], - 'db' => [ + 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=172.17.0.2', 'username' => 'root', 'password' => 'swoft123456', ], - 'db2' => [ + 'db2' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=172.17.0.2', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, 'database' => bean('db2') ], - 'db3' => [ + 'db3' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=172.17.0.2', 'username' => 'root', 'password' => 'swoft123456' ], - 'db3.pool' => [ + 'db3.pool' => [ 'class' => Pool::class, 'database' => bean('db3') ], 'migrationManager' => [ 'migrationPath' => '@app/Migration', ], - 'redis' => [ + 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, + 'option' => [ + 'prefix' => "swoft" + ] ], - 'user' => [ + 'user' => [ 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, @@ -97,14 +100,14 @@ ], 'packet' => bean('rpcClientPacket') ], - 'user.pool' => [ + 'user.pool' => [ 'class' => ServicePool::class, 'client' => bean('user') ], - 'rpcServer' => [ + 'rpcServer' => [ 'class' => ServiceServer::class, ], - 'wsServer' => [ + 'wsServer' => [ 'class' => WebSocketServer::class, 'on' => [ // Enable http handle @@ -116,7 +119,7 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], - 'cliRouter' => [ + 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], ] ]; From 930bbd04dd5aed767af4978f7ac62cf54163d2c2 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 18 Jul 2019 17:06:54 +0800 Subject: [PATCH 460/643] Update swooleEvent --- app/Listener/DeregisterServiceListener.php | 2 +- app/Listener/RegisterServiceListener.php | 2 +- app/bean.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Listener/DeregisterServiceListener.php b/app/Listener/DeregisterServiceListener.php index 78ace1ae..5aab5a6b 100644 --- a/app/Listener/DeregisterServiceListener.php +++ b/app/Listener/DeregisterServiceListener.php @@ -15,7 +15,7 @@ use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; use Swoft\Http\Server\HttpServer; -use Swoft\Server\Swoole\SwooleEvent; +use Swoft\Server\SwooleEvent; use Swoole\Coroutine; /** diff --git a/app/Listener/RegisterServiceListener.php b/app/Listener/RegisterServiceListener.php index 9bdce35d..e7e1f90a 100644 --- a/app/Listener/RegisterServiceListener.php +++ b/app/Listener/RegisterServiceListener.php @@ -12,7 +12,7 @@ use Swoft\Event\EventInterface; use Swoft\Http\Server\HttpServer; use Swoft\Log\Helper\CLog; -use Swoft\Server\Swoole\SwooleEvent; +use Swoft\Server\SwooleEvent; use Swoole\Coroutine; /** diff --git a/app/bean.php b/app/bean.php index 5300227c..4cdf55d1 100644 --- a/app/bean.php +++ b/app/bean.php @@ -12,7 +12,7 @@ use Swoft\Rpc\Server\ServiceServer; use Swoft\Http\Server\Swoole\RequestListener; use Swoft\WebSocket\Server\WebSocketServer; -use Swoft\Server\Swoole\SwooleEvent; +use Swoft\Server\SwooleEvent; use Swoft\Db\Database; use Swoft\Redis\RedisDb; From 80b5541ca9ddf1bbb39bcaf674d69e51bfcbab65 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 18 Jul 2019 17:18:08 +0800 Subject: [PATCH 461/643] Add log demo --- app/Http/Controller/LogController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Controller/LogController.php b/app/Http/Controller/LogController.php index aedcb8d2..121d4f5a 100644 --- a/app/Http/Controller/LogController.php +++ b/app/Http/Controller/LogController.php @@ -7,6 +7,7 @@ use Swoft\Bean\Exception\ContainerException; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\Log\Helper\CLog; use Swoft\Log\Helper\Log; /** @@ -41,6 +42,10 @@ public function test(): array Log::alert('this %s log', 'alert'); Log::emergency('this %s log', 'emergency'); + Log::error('Special message user@%'); + + CLog::info('Special message user@%'); + // Tag end Log::profileEnd('tagName'); From dd912f6fbc41a6a05cb91e5fae666ad2e5a1a0ba Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Thu, 18 Jul 2019 18:24:09 +0800 Subject: [PATCH 462/643] add demo --- app/Http/Controller/RedisController.php | 17 +++++++++++++++-- app/Listener/RanListener.php | 4 +--- app/Process/MonitorProcess.php | 2 +- app/Process/Worker1Process.php | 2 +- app/Process/Worker2Process.php | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index f30d19db..3ae8dfba 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -40,7 +40,13 @@ public function poolSet(): array $get = $this->redis->get($key); - return [$get, $value]; + $isError = $this->redis->call(function (\Redis $redis) { + $redis->eval('returnxxxx 1'); + + return $redis->getLastError(); + }); + + return [$get, $value, $isError]; } /** @@ -73,9 +79,16 @@ public function str(): array $keyVal = Redis::get($key); + $isError = Redis::call(function (\Redis $redis) { + $redis->eval('return 1'); + + return $redis->getLastError(); + }); + $data = [ $result, - $keyVal + $keyVal, + $isError ]; return $data; diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index 829ce42c..aa235707 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -56,9 +56,7 @@ public function getRawSql(string $sql, array $bindings, Connection $connection) return $sql; } foreach ($bindings as $name => $value) { - if (is_string($name) && strncmp(':', $name, 1)) { - $name = ':' . $name; - } else { + if (is_int($name)) { $name = '?'; } diff --git a/app/Process/MonitorProcess.php b/app/Process/MonitorProcess.php index d9b1650a..2af256ad 100644 --- a/app/Process/MonitorProcess.php +++ b/app/Process/MonitorProcess.php @@ -36,4 +36,4 @@ public function run(Process $process): void { $this->logic->monitor($process); } -} \ No newline at end of file +} diff --git a/app/Process/Worker1Process.php b/app/Process/Worker1Process.php index c802ff73..b90a6969 100644 --- a/app/Process/Worker1Process.php +++ b/app/Process/Worker1Process.php @@ -31,4 +31,4 @@ public function run(Pool $pool, int $workerId): void Coroutine::sleep(3); } } -} \ No newline at end of file +} diff --git a/app/Process/Worker2Process.php b/app/Process/Worker2Process.php index a811174f..a4bd00ce 100644 --- a/app/Process/Worker2Process.php +++ b/app/Process/Worker2Process.php @@ -45,4 +45,4 @@ public function run(Pool $pool, int $workerId): void Coroutine::sleep(3); } } -} \ No newline at end of file +} From 1623528e2186070df4223b789b23d2a952a18026 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 18 Jul 2019 20:23:20 +0800 Subject: [PATCH 463/643] Add Listener test --- app/Listener/Test/ShutDownListener.php | 31 +++++++++++++++++++++++ app/Listener/Test/StartListener.php | 31 +++++++++++++++++++++++ app/Listener/Test/TaskProcessListener.php | 29 +++++++++++++++++++++ app/Listener/Test/WorkerStartListener.php | 30 ++++++++++++++++++++++ app/Listener/Test/WorkerStopListener.php | 31 +++++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 app/Listener/Test/ShutDownListener.php create mode 100644 app/Listener/Test/StartListener.php create mode 100644 app/Listener/Test/TaskProcessListener.php create mode 100644 app/Listener/Test/WorkerStartListener.php create mode 100644 app/Listener/Test/WorkerStopListener.php diff --git a/app/Listener/Test/ShutDownListener.php b/app/Listener/Test/ShutDownListener.php new file mode 100644 index 00000000..f7515cad --- /dev/null +++ b/app/Listener/Test/ShutDownListener.php @@ -0,0 +1,31 @@ + Date: Thu, 18 Jul 2019 22:11:00 +0800 Subject: [PATCH 464/643] update some --- app/Tcp/Controller/DemoController.php | 35 +++++++++++++++++- app/bean.php | 53 ++++++++++++++------------- 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/app/Tcp/Controller/DemoController.php b/app/Tcp/Controller/DemoController.php index 3c8734a8..b5e635c5 100644 --- a/app/Tcp/Controller/DemoController.php +++ b/app/Tcp/Controller/DemoController.php @@ -4,6 +4,8 @@ use Swoft\Tcp\Server\Annotation\Mapping\TcpController; use Swoft\Tcp\Server\Annotation\Mapping\TcpMapping; +use Swoft\Tcp\Server\Request; +use Swoft\Tcp\Server\Response; /** * Class DemoController @@ -12,19 +14,48 @@ */ class DemoController { + /** + * @TcpMapping("list", root=true) + * @param Response $response + */ + public function list(Response $response): void + { + $response->setData('[list]allow command: list, echo, demo.echo'); + } + /** * @TcpMapping("echo") + * @param Request $request + * @param Response $response + */ + public function index(Request $request, Response $response): void + { + $str = $request->getPackage()->getDataString(); + + $response->setData('[demo.echo]hi, we received your message: ' . $str); + } + + /** + * @TcpMapping("strrev", root=true) + * @param Request $request + * @param Response $response */ - public function index(): void + public function strRev(Request $request, Response $response): void { + $str = $request->getPackage()->getDataString(); + $response->setData(\strrev($str)); } /** * @TcpMapping("echo", root=true) + * @param Request $request + * @param Response $response */ - public function echo(): void + public function echo(Request $request, Response $response): void { + $str = $request->getRawData(); + $response->setData('[echo]hi, we received your message: ' . $str); } } diff --git a/app/bean.php b/app/bean.php index 617b24f0..46cec974 100644 --- a/app/bean.php +++ b/app/bean.php @@ -1,26 +1,26 @@ [ + 'logger' => [ 'flushRequest' => true, 'enable' => false, 'json' => false, ], - 'httpServer' => [ + 'httpServer' => [ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ @@ -36,50 +36,50 @@ 'task_enable_coroutine' => true ] ], - 'httpDispatcher' => [ + 'httpDispatcher' => [ // Add global http middleware 'middlewares' => [ // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], ], - 'db' => [ + 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=192.168.4.11', 'username' => 'root', 'password' => 'swoft123456', ], - 'db2' => [ + 'db2' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=192.168.4.11', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, 'database' => bean('db2') ], - 'db3' => [ + 'db3' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=192.168.4.11', 'username' => 'root', 'password' => 'swoft123456' ], - 'db3.pool' => [ + 'db3.pool' => [ 'class' => Pool::class, 'database' => bean('db3') ], - 'migrationManager' => [ + 'migrationManager' => [ 'migrationPath' => '@app/Migration', ], - 'redis' => [ + 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ], - 'user' => [ + 'user' => [ 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, @@ -91,16 +91,16 @@ ], 'packet' => bean('rpcClientPacket') ], - 'user.pool' => [ + 'user.pool' => [ 'class' => ServicePool::class, 'client' => bean('user') ], - 'rpcServer' => [ + 'rpcServer' => [ 'class' => ServiceServer::class, ], - 'wsServer' => [ + 'wsServer' => [ 'class' => WebSocketServer::class, - 'port' => 18308, + 'port' => 18308, 'on' => [ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), @@ -111,13 +111,16 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], - 'tcpServer' => [ - 'port' => 18309, + 'tcpServer' => [ + 'port' => 18309, + 'debug' => 1, ], + /** @see \Swoft\Tcp\Protocol */ 'tcpServerProtocol' => [ - 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + // 'openLengthCheck' => true, ], - 'cliRouter' => [ + 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], ] ]; From 840aab9ba8f030ec00f057b08e58f4e39d16fca8 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 18 Jul 2019 23:15:23 +0800 Subject: [PATCH 465/643] add --- app/bean.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/bean.php b/app/bean.php index 670d86ec..83d87c64 100644 --- a/app/bean.php +++ b/app/bean.php @@ -84,13 +84,8 @@ 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, -<<<<<<< HEAD 'option' => [ - 'prefix' => 'swoft:prefix:' -======= - 'option' => [ - 'prefix' => "swoft" ->>>>>>> 31b0690969adf32c1a57c7c63a21052f6c377f91 + 'prefix' => 'swoft:' ] ], 'user' => [ From 46f6c3e2fea791552f325bd94ea046061f22f523 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 18 Jul 2019 23:41:54 +0800 Subject: [PATCH 466/643] update home page --- .editorconfig | 2 +- resource/views/home/index.php | 401 ++++++++++++++++++++-------------- 2 files changed, 236 insertions(+), 167 deletions(-) diff --git a/.editorconfig b/.editorconfig index b59a85f8..8a4c76fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,6 +16,6 @@ trim_trailing_whitespace = false [*.php] indent_size = 4 -[resource/views/*.php] +[resource/views/*/*.php] indent_size = 2 diff --git a/resource/views/home/index.php b/resource/views/home/index.php index 2bd08446..b69d4055 100644 --- a/resource/views/home/index.php +++ b/resource/views/home/index.php @@ -1,175 +1,244 @@ - - - - Swoft Framework 2.0 - + + + + Swoft Framework 2.0 - PHP microservices coroutine framework +
      -

      Swoft Framework

      + +
      +

      Swoft Framework + 2.x +

      +
      + From e7eb4205977d1d8be662d9e65c0f499c2773a361 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 18 Jul 2019 23:55:08 +0800 Subject: [PATCH 467/643] Add push --- app/Console/Command/TestCommand.php | 2 +- app/Listener/RanListener.php | 2 +- app/bean.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 8130b7c5..8a113171 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -104,7 +104,7 @@ private function uris(): array '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', - '/rpc/sendBigString', +// '/rpc/sendBigString', '/rpc/returnNull' ], 'co' => [ diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index aa235707..6bf3a49e 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -36,7 +36,7 @@ public function handle(EventInterface $event): void $rawSql = $this->getRawSql($querySql, $bindings, $connection); - output()->info($rawSql); +// output()->info($rawSql); } /** diff --git a/app/bean.php b/app/bean.php index 83d87c64..5f3398b8 100644 --- a/app/bean.php +++ b/app/bean.php @@ -18,8 +18,8 @@ return [ 'logger' => [ - 'flushRequest' => true, - 'enable' => true, + 'flushRequest' => false, + 'enable' => false, 'json' => false, ], 'httpServer' => [ From 1dbdec54aec6e0e6dfa887e4ee2b8d83377609fc Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 19 Jul 2019 19:29:04 +0800 Subject: [PATCH 468/643] Add process --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 1d0aec11..10a6985c 100644 --- a/composer.json +++ b/composer.json @@ -25,6 +25,7 @@ "swoft/rpc-server": "~2.0.0", "swoft/websocket-server": "~2.0.0", "swoft/tcp-server": "~2.0.0", + "swoft/process": "~2.0.0", "swoft/apollo": "~2.0.0", "swoft/consul": "~2.0.0", "swoft/limiter": "~2.0.0", From 8d81583449194727cb207d449c3169eb1d5bac1d Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 19 Jul 2019 19:33:34 +0800 Subject: [PATCH 469/643] Modify swoole version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4eb911d5..375286a2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) [![Docker Build Status](https://img.shields.io/docker/build/swoft/swoft.svg)](https://hub.docker.com/r/swoft/swoft/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) -[![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) +[![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.4.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) [![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) From 446589e5df5b8d4f13e728e167bc8b73c1438b2c Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 19 Jul 2019 20:54:11 +0800 Subject: [PATCH 470/643] upgrade swoole to 4.4.1 --- Dockerfile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8bec0c16..91cae2fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,14 +23,11 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.3.5 \ + SWOOLE_VERSION=4.4.1 \ COMPOSER_ALLOW_SUPERUSER=1 -# Timezone -RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ - && echo 'Asia/Shanghai' > /etc/timezone \ # Libs -y --no-install-recommends - && apt-get update \ +RUN apt-get update \ && apt-get install -y \ curl wget git zip unzip less vim openssl \ libz-dev \ From 22e7a906527314f470fa4d2863cb00592f4081d8 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 25 Jul 2019 19:49:08 +0800 Subject: [PATCH 471/643] update some for tcp demo --- app/Tcp/Controller/DemoController.php | 6 ++++-- app/bean.php | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Tcp/Controller/DemoController.php b/app/Tcp/Controller/DemoController.php index b5e635c5..e0f0a629 100644 --- a/app/Tcp/Controller/DemoController.php +++ b/app/Tcp/Controller/DemoController.php @@ -6,6 +6,7 @@ use Swoft\Tcp\Server\Annotation\Mapping\TcpMapping; use Swoft\Tcp\Server\Request; use Swoft\Tcp\Server\Response; +use function strrev; /** * Class DemoController @@ -44,7 +45,7 @@ public function strRev(Request $request, Response $response): void { $str = $request->getPackage()->getDataString(); - $response->setData(\strrev($str)); + $response->setData(strrev($str)); } /** @@ -54,7 +55,8 @@ public function strRev(Request $request, Response $response): void */ public function echo(Request $request, Response $response): void { - $str = $request->getRawData(); + // $str = $request->getRawData(); + $str = $request->getPackage()->getDataString(); $response->setData('[echo]hi, we received your message: ' . $str); } diff --git a/app/bean.php b/app/bean.php index 5f3398b8..5a715efd 100644 --- a/app/bean.php +++ b/app/bean.php @@ -126,7 +126,8 @@ ], /** @see \Swoft\Tcp\Protocol */ 'tcpServerProtocol' => [ - 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, + // 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, // 'openLengthCheck' => true, ], 'cliRouter' => [ From ac835dc84cf1a43e795cc036585e2cd399f695e1 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 25 Jul 2019 19:54:03 +0800 Subject: [PATCH 472/643] format some code --- app/AutoLoader.php | 4 +--- app/Exception/Handler/ApiExceptionHandler.php | 7 +++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/AutoLoader.php b/app/AutoLoader.php index 8e72de28..c04475c2 100644 --- a/app/AutoLoader.php +++ b/app/AutoLoader.php @@ -1,9 +1,7 @@ sprintf('At %s line %d', $except->getFile(), $except->getLine()), 'trace' => $except->getTraceAsString(), ]; - return $response->withData($data); + + return $response->withData($data); } } From b35fa285bdf1353a57136995c85433a50e10700e Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 25 Jul 2019 20:54:11 +0800 Subject: [PATCH 473/643] log error message on http error --- app/Exception/Handler/HttpExceptionHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 19571b3f..10d22edd 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -11,6 +11,7 @@ use Swoft\Http\Message\Response; use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; use Swoft\Log\Helper\CLog; +use Swoft\Log\Helper\Log; use Throwable; /** @@ -31,6 +32,7 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler public function handle(Throwable $e, Response $response): Response { // Log + Log::error($e->getMessage()); CLog::error($e->getMessage()); // Debug is false From 32c76d1fa09744bf34f171fda479709b912dc4a1 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 25 Jul 2019 23:09:11 +0800 Subject: [PATCH 474/643] add an default http middleware --- app/Http/Middleware/ControllerMiddleware.php | 10 ------ app/Http/Middleware/FavIconMiddleware.php | 38 ++++++++++++++++++++ app/bean.php | 1 + 3 files changed, 39 insertions(+), 10 deletions(-) delete mode 100644 app/Http/Middleware/ControllerMiddleware.php create mode 100644 app/Http/Middleware/FavIconMiddleware.php diff --git a/app/Http/Middleware/ControllerMiddleware.php b/app/Http/Middleware/ControllerMiddleware.php deleted file mode 100644 index d6097b5e..00000000 --- a/app/Http/Middleware/ControllerMiddleware.php +++ /dev/null @@ -1,10 +0,0 @@ -getUriPath() === '/favicon.ico') { + return context()->getResponse()->withStatus(404); + } + + return $handler->handle($request); + } +} diff --git a/app/bean.php b/app/bean.php index 5a715efd..ae7d54ca 100644 --- a/app/bean.php +++ b/app/bean.php @@ -45,6 +45,7 @@ 'httpDispatcher' => [ // Add global http middleware 'middlewares' => [ + \App\Http\Middleware\FavIconMiddleware::class, // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], From cfa8cea07337ba8631e98a413f57ac66825044b7 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 25 Jul 2019 23:58:36 +0800 Subject: [PATCH 475/643] update some --- app/Http/Middleware/FavIconMiddleware.php | 1 - resource/views/home/index.php | 41 ++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/app/Http/Middleware/FavIconMiddleware.php b/app/Http/Middleware/FavIconMiddleware.php index 1ae2ea80..3e149ae9 100644 --- a/app/Http/Middleware/FavIconMiddleware.php +++ b/app/Http/Middleware/FavIconMiddleware.php @@ -1,6 +1,5 @@ @@ -227,7 +260,7 @@
      -

      Swoft Framework +

      Swoft Framework 2.x

      From 423472bddb3042901bf0ab7c7ac8b8f4572b7c7e Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 29 Jul 2019 17:07:21 +0800 Subject: [PATCH 476/643] upgrade swoole to 4.4.2 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 91cae2fd..1b210d38 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.4.1 \ + SWOOLE_VERSION=4.4.2 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From 2539a3e787ad62d2e8fc49652dbdcd905e8571ec Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 30 Jul 2019 17:47:17 +0800 Subject: [PATCH 477/643] Add timer dmeo --- app/Http/Controller/TimerController.php | 79 +++++++++++++++++++++++++ app/bean.php | 10 ++-- 2 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 app/Http/Controller/TimerController.php diff --git a/app/Http/Controller/TimerController.php b/app/Http/Controller/TimerController.php new file mode 100644 index 00000000..d8378a8b --- /dev/null +++ b/app/Http/Controller/TimerController.php @@ -0,0 +1,79 @@ +setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + $id = $user->getId(); + + Redis::set("$id", $user->toArray()); + Log::info("用户ID=" . $id . " timerId=" . $timerId); + sgo(function () use ($id) { + $user = User::find($id)->toArray(); + Log::info(JsonHelper::encode($user)); + Redis::del("$id"); + }); + }); + + return ['after']; + } + + /** + * @RequestMapping() + * + * @return array + * @throws Exception + */ + public function tick(): array + { + Timer::tick(3 * 1000, function (int $timerId) { + $user = new User(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + $id = $user->getId(); + + Redis::set("$id", $user->toArray()); + Log::info("用户ID=" . $id . " timerId=" . $timerId); + sgo(function () use ($id) { + $user = User::find($id)->toArray(); + Log::info(JsonHelper::encode($user)); + Redis::del("$id"); + }); + }); + + return ['tick']; + } +} \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 5f3398b8..f76083a5 100644 --- a/app/bean.php +++ b/app/bean.php @@ -18,8 +18,8 @@ return [ 'logger' => [ - 'flushRequest' => false, - 'enable' => false, + 'flushRequest' => true, + 'enable' => true, 'json' => false, ], 'httpServer' => [ @@ -51,13 +51,13 @@ ], 'db' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test;host=172.17.0.2', + 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', ], 'db2' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=172.17.0.2', + 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) @@ -68,7 +68,7 @@ ], 'db3' => [ 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=172.17.0.2', + 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456' ], From 376346e3ad4935a027813ad44579efe90e3fa109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 31 Jul 2019 05:01:55 +0000 Subject: [PATCH 478/643] Introduce PHPStan --- .travis.yml | 4 +++- app/Console/Command/AgentCommand.php | 2 +- app/Http/Controller/BeanController.php | 2 +- app/Http/Controller/DbModelController.php | 2 +- app/Http/Controller/RespController.php | 2 +- app/Listener/DeregisterServiceListener.php | 2 +- app/Listener/ModelSavedListener.php | 2 +- app/Listener/RanListener.php | 2 +- app/Listener/RegisterServiceListener.php | 2 +- app/Listener/UserSavingListener.php | 2 +- app/WebSocket/Chat/HomeController.php | 4 ++-- composer.json | 5 ++++- phpstan.neon.dist | 16 ++++++++++++++++ test/bootstrap.php | 1 + 14 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 phpstan.neon.dist diff --git a/.travis.yml b/.travis.yml index 07ca29df..d4cd3523 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,10 @@ php: - 7.1 - 7.2 - 7.3 + services: - redis + install: - echo 'no' | pecl install -f redis - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - @@ -13,8 +15,8 @@ install: before_script: - composer config -g process-timeout 900 && composer update + - composer require --dev phpstan/phpstan-shim - phpenv config-rm xdebug.ini - script: - composer test diff --git a/app/Console/Command/AgentCommand.php b/app/Console/Command/AgentCommand.php index 86fec6e1..2e9819e7 100644 --- a/app/Console/Command/AgentCommand.php +++ b/app/Console/Command/AgentCommand.php @@ -76,7 +76,7 @@ public function updateConfigFile(array $data): void // $server = bean('rpcServer'); // $server->restart(); - /* @var WebSocketServer $server */ + /** @var WebSocketServer $server */ $server = bean('wsServer'); $server->restart(); } diff --git a/app/Http/Controller/BeanController.php b/app/Http/Controller/BeanController.php index 5dc0d584..b9610e5f 100644 --- a/app/Http/Controller/BeanController.php +++ b/app/Http/Controller/BeanController.php @@ -32,7 +32,7 @@ public function request(): array { $id = (string)Co::tid(); - /* @var RequestBean $request*/ + /** @var RequestBean $request*/ $request = BeanFactory::getRequestBean('requestBean', $id); return $request->getData(); } diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 0728feb6..6f5fc97c 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -130,7 +130,7 @@ public function batchUpdate() $users = User::find(array_column($values, 'id')); $updateResults = []; - /* @var User $user */ + /** @var User $user */ foreach ($users as $user) { $updateResults[$user->getId()] = true; if ($user->getAge() != $values[$user->getId()]['age']) { diff --git a/app/Http/Controller/RespController.php b/app/Http/Controller/RespController.php index 5527b754..0afa02d6 100644 --- a/app/Http/Controller/RespController.php +++ b/app/Http/Controller/RespController.php @@ -24,7 +24,7 @@ class RespController */ public function cookie(): Response { - /* @var Response $resp */ + /** @var Response $resp */ $resp = Context::mustGet()->getResponse(); return $resp->setCookie('c-name', 'c-value')->withData(['hello']); diff --git a/app/Listener/DeregisterServiceListener.php b/app/Listener/DeregisterServiceListener.php index 5aab5a6b..84ebee42 100644 --- a/app/Listener/DeregisterServiceListener.php +++ b/app/Listener/DeregisterServiceListener.php @@ -44,7 +44,7 @@ class DeregisterServiceListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - /* @var HttpServer $httpServer */ + /** @var HttpServer $httpServer */ $httpServer = $event->getTarget(); // $this->agent->deregisterService('swoft'); diff --git a/app/Listener/ModelSavedListener.php b/app/Listener/ModelSavedListener.php index 0741ff82..d9cc9be0 100644 --- a/app/Listener/ModelSavedListener.php +++ b/app/Listener/ModelSavedListener.php @@ -24,7 +24,7 @@ class ModelSavedListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - /* @var Model $modelStatic */ + /** @var Model $modelStatic */ $modelStatic = $event->getTarget(); if ($modelStatic instanceof User) { diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index 6bf3a49e..c1c5bcea 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -28,7 +28,7 @@ class RanListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - /* @var Connection $connection */ + /** @var Connection $connection */ $connection = $event->getTarget(); $querySql = $event->getParam(0); diff --git a/app/Listener/RegisterServiceListener.php b/app/Listener/RegisterServiceListener.php index e7e1f90a..24c84089 100644 --- a/app/Listener/RegisterServiceListener.php +++ b/app/Listener/RegisterServiceListener.php @@ -36,7 +36,7 @@ class RegisterServiceListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - /* @var HttpServer $httpServer */ + /** @var HttpServer $httpServer */ $httpServer = $event->getTarget(); $service = [ diff --git a/app/Listener/UserSavingListener.php b/app/Listener/UserSavingListener.php index cf278e4f..d92b01a6 100644 --- a/app/Listener/UserSavingListener.php +++ b/app/Listener/UserSavingListener.php @@ -23,7 +23,7 @@ class UserSavingListener implements EventHandlerInterface public function handle(EventInterface $event): void { - /* @var User $user */ + /** @var User $user */ $user = $event->getTarget(); /** diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php index ef2ac788..6360cb0d 100644 --- a/app/WebSocket/Chat/HomeController.php +++ b/app/WebSocket/Chat/HomeController.php @@ -27,7 +27,7 @@ public function index(): void /** * Message command is: 'home.echo' * - * @param $data + * @param string $data * @MessageMapping() */ public function echo($data): void @@ -38,7 +38,7 @@ public function echo($data): void /** * Message command is: 'home.ar' * - * @param $data + * @param string $data * @MessageMapping("ar") * * @return string diff --git a/composer.json b/composer.json index 10a6985c..8eeb1c02 100644 --- a/composer.json +++ b/composer.json @@ -54,7 +54,10 @@ "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], - "test": "./vendor/bin/phpunit -c phpunit.xml", + "test": [ + "./vendor/bin/phpstan analyze", + "./vendor/bin/phpunit -c phpunit.xml", + ], "cs-fix": "./vendor/bin/php-cs-fixer fix $1" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..458cf0ce --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,16 @@ +includes: + - phar://phpstan.phar/conf/bleedingEdge.neon +parameters: + level: max + inferPrivatePropertyTypeFromConstructor: true + paths: + - %currentWorkingDirectory%/app/ + autoload_files: + - %currentWorkingDirectory%/test/bootstrap.php + autoload_directories: + - %currentWorkingDirectory%/vendor/swoft/swoole-ide-helper/src/namespace/ + dynamicConstantNames: + - APP_DEBUG + ignoreErrors: + # Variable type + - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' diff --git a/test/bootstrap.php b/test/bootstrap.php index a4abe2da..ba7b26ad 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,2 +1,3 @@ Date: Wed, 31 Jul 2019 05:04:45 +0000 Subject: [PATCH 479/643] Fix composer typo --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8eeb1c02..7d44d5bf 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ ], "test": [ "./vendor/bin/phpstan analyze", - "./vendor/bin/phpunit -c phpunit.xml", + "./vendor/bin/phpunit -c phpunit.xml" ], "cs-fix": "./vendor/bin/php-cs-fixer fix $1" } From e94e2d07948a9d5c3210768f8e1035c93201ebdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Thu, 1 Aug 2019 05:36:20 +0200 Subject: [PATCH 480/643] Ignore current 17 static analysis errors @inhere https://github.com/swoft-cloud/swoft/pull/864#issuecomment-516722647 --- phpstan.neon.dist | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 458cf0ce..4a69c118 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -14,3 +14,40 @@ parameters: ignoreErrors: # Variable type - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' + # These are ignored for now + - + path: %currentWorkingDirectory%/app/Exception/Handler/WsMessageExceptionHandler.php + message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' + - + path: %currentWorkingDirectory%/app/Http/Controller/DbModelController.php + message: '#^Method App\\Http\\Controller\\DbModelController::getId\(\) should return int but returns int\|null\.$#' + - + path: %currentWorkingDirectory%/app/Http/Controller/DbModelController.php + message: '#^Argument of an invalid type App\\Model\\Entity\\User supplied for foreach, only iterables are supported\.$#' + - + path: %currentWorkingDirectory%/app/Http/Controller/DbTransactionController.php + message: '#^Method App\\Http\\Controller\\DbTransactionController::getId\(\) should return int but returns int\|null\.$#' + - + path: %currentWorkingDirectory%/app/Http/Controller/RpcController.php + message: '#^Unreachable statement - code above always terminates\.$#' + - + path: %currentWorkingDirectory%/app/Http/Controller/SelectDbController.php + message: '#^Method App\\Http\\Controller\\SelectDbController::getId\(\) should return int but returns int\|null\.$#' + - + path: %currentWorkingDirectory%/app/Http/Controller/ValidatorController.php + message: '#^Method App\\Http\\Controller\\ValidatorController::(validateAll|validateType|validatePassword|validateCustomer)\(\) should return array but returns array\|object\|null\.$#' + - + path: %currentWorkingDirectory%/app/Task/Task/SyncTask.php + message: '#^Method App\\Task\\Task\\SyncTask::testNull\(\) should return bool but returns null\.$#' + - + path: %currentWorkingDirectory%/app/Validator/Rule/AlphaDashRule.php + message: '#^Call to an undefined method object::getMessage\(\)\.$#' + - + path: %currentWorkingDirectory%/app/WebSocket/Chat/HomeController.php + message: '#^Call to an undefined method Swoft\\Session\\SessionInterface::push\(\)\.$#' + - + path: %currentWorkingDirectory%/app/WebSocket/ChatModule.php + message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' + - + path: %currentWorkingDirectory%/app/WebSocket/EchoModule.php + message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' From 55d0dfc2561af639ccf4941f4e91e7f49ed221cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Thu, 1 Aug 2019 04:05:02 +0000 Subject: [PATCH 481/643] Check coding style --- .gitignore | 1 + .travis.yml | 14 ++++++++++---- composer.json | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index fa561a74..c9e57566 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ vendor/ .phpstorm.meta.php .DS_Store public/devtool/ +bin/php-cs-fixer diff --git a/.travis.yml b/.travis.yml index d4cd3523..96b02161 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,14 +9,20 @@ services: - redis install: - - echo 'no' | pecl install -f redis - - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - - - echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - | + echo "no" | pecl install -f redis + - | + wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - + echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - | + wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" + chmod +x bin/php-cs-fixer before_script: + - phpenv config-rm xdebug.ini - composer config -g process-timeout 900 && composer update - composer require --dev phpstan/phpstan-shim - - phpenv config-rm xdebug.ini script: + - composer cs-fix - composer test diff --git a/composer.json b/composer.json index 7d44d5bf..08654cc2 100644 --- a/composer.json +++ b/composer.json @@ -58,6 +58,6 @@ "./vendor/bin/phpstan analyze", "./vendor/bin/phpunit -c phpunit.xml" ], - "cs-fix": "./vendor/bin/php-cs-fixer fix $1" + "cs-fix": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff" } } From 416f57f4954a5286d0d2db5b8e7dd6191ef9e6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Thu, 1 Aug 2019 04:13:44 +0000 Subject: [PATCH 482/643] Remove swool extension directory as is contains a PHP file breaking php-cs-fixer --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 96b02161..4d7fae11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ install: - | echo "no" | pecl install -f redis - | - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - + wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - | wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" From b51c6224e4afeb21285c9dbe623111e568f65cc9 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 1 Aug 2019 16:02:15 +0800 Subject: [PATCH 483/643] Add request demo --- app/Console/Command/TestCommand.php | 3 ++- app/Http/Controller/BeanController.php | 19 ++++++++++++++++++- app/Model/Logic/RequestBeanTwo.php | 24 ++++++++++++++++++++++++ app/bean.php | 4 ++-- composer.json | 2 ++ 5 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 app/Model/Logic/RequestBeanTwo.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 8a113171..90b4b666 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -111,7 +111,8 @@ private function uris(): array '/co/multi' ], 'bean' => [ - '/bean/request' + '/bean/request', + '/bean/requestClass' ], 'breaker' => [ '/breaker/unbreak', diff --git a/app/Http/Controller/BeanController.php b/app/Http/Controller/BeanController.php index 5dc0d584..f08ae817 100644 --- a/app/Http/Controller/BeanController.php +++ b/app/Http/Controller/BeanController.php @@ -4,6 +4,7 @@ namespace App\Http\Controller; use App\Model\Logic\RequestBean; +use App\Model\Logic\RequestBeanTwo; use ReflectionException; use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Bean\BeanFactory; @@ -32,8 +33,24 @@ public function request(): array { $id = (string)Co::tid(); - /* @var RequestBean $request*/ + /* @var RequestBean $request */ $request = BeanFactory::getRequestBean('requestBean', $id); return $request->getData(); } + + /** + * @return array + * @throws ContainerException + * @throws ReflectionException + * + * @RequestMapping() + */ + public function requestClass(): array + { + $id = (string)Co::tid(); + + /* @var RequestBeanTwo $request */ + $request = BeanFactory::getRequestBean(RequestBeanTwo::class, $id); + return $request->getData(); + } } \ No newline at end of file diff --git a/app/Model/Logic/RequestBeanTwo.php b/app/Model/Logic/RequestBeanTwo.php new file mode 100644 index 00000000..8243d822 --- /dev/null +++ b/app/Model/Logic/RequestBeanTwo.php @@ -0,0 +1,24 @@ + [ - 'flushRequest' => true, - 'enable' => true, + 'flushRequest' => false, + 'enable' => false, 'json' => false, ], 'httpServer' => [ diff --git a/composer.json b/composer.json index 10a6985c..ac3f41dd 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,8 @@ "ext-pdo": "*", "ext-redis": "*", "ext-json": "*", + "ext-simplexml": "*", + "ext-libxml": "*", "ext-mbstring": "*", "swoft/db": "~2.0.0", "swoft/i18n": "~2.0.0", From 7c410e45b2d3d5fb7b48f3c910c48b0d53907601 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Thu, 1 Aug 2019 16:25:47 +0800 Subject: [PATCH 484/643] Validator not must --- app/bean.php | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/app/bean.php b/app/bean.php index ac1f2663..30b5b0c2 100644 --- a/app/bean.php +++ b/app/bean.php @@ -17,18 +17,18 @@ use Swoft\Redis\RedisDb; return [ - 'logger' => [ + 'logger' => [ 'flushRequest' => false, 'enable' => false, 'json' => false, ], - 'httpServer' => [ + 'httpServer' => [ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ 'rpc' => bean('rpcServer') ], - 'process' => [ + 'process' => [ // 'monitor' => bean(MonitorProcess::class) ], 'on' => [ @@ -42,53 +42,56 @@ 'task_enable_coroutine' => true ] ], - 'httpDispatcher' => [ + 'httpDispatcher' => [ // Add global http middleware - 'middlewares' => [ + 'middlewares' => [ // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], + 'afterMiddlewares' => [ + \Swoft\Http\Server\Middleware\ValidatorMiddleware::class + ] ], - 'db' => [ + 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', ], - 'db2' => [ + 'db2' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, 'database' => bean('db2') ], - 'db3' => [ + 'db3' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456' ], - 'db3.pool' => [ + 'db3.pool' => [ 'class' => Pool::class, 'database' => bean('db3') ], - 'migrationManager' => [ + 'migrationManager' => [ 'migrationPath' => '@app/Migration', ], - 'redis' => [ + 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, - 'option' => [ + 'option' => [ 'prefix' => 'swoft:' ] ], - 'user' => [ + 'user' => [ 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, @@ -100,14 +103,14 @@ ], 'packet' => bean('rpcClientPacket') ], - 'user.pool' => [ + 'user.pool' => [ 'class' => ServicePool::class, 'client' => bean('user') ], - 'rpcServer' => [ + 'rpcServer' => [ 'class' => ServiceServer::class, ], - 'wsServer' => [ + 'wsServer' => [ 'class' => WebSocketServer::class, 'port' => 18308, 'on' => [ @@ -126,7 +129,7 @@ ], /** @see \Swoft\Tcp\Protocol */ 'tcpServerProtocol' => [ - 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, // 'openLengthCheck' => true, ], 'cliRouter' => [ From 72824025a0295d17169a895c2569b12577a2904f Mon Sep 17 00:00:00 2001 From: JasonYHZ Date: Fri, 2 Aug 2019 18:24:26 +0800 Subject: [PATCH 485/643] add Crontab Example --- app/Crontab/CronTask.php | 31 +++++++++++++++++++++++++++++++ app/bean.php | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 app/Crontab/CronTask.php diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php new file mode 100644 index 00000000..75ae2b65 --- /dev/null +++ b/app/Crontab/CronTask.php @@ -0,0 +1,31 @@ + [ @@ -30,6 +31,7 @@ ], 'process' => [ // 'monitor' => bean(MonitorProcess::class) +// 'crontab' => bean(Crontab::class) ], 'on' => [ // SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task From a2fa75e733e058b272202b6842a98cbbf04454aa Mon Sep 17 00:00:00 2001 From: JasonYHZ Date: Fri, 2 Aug 2019 18:26:31 +0800 Subject: [PATCH 486/643] fix code format --- app/Crontab/CronTask.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index 75ae2b65..2e536c3f 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -1,5 +1,4 @@ - Date: Fri, 2 Aug 2019 20:45:53 +0800 Subject: [PATCH 487/643] import Annotaion --- app/Crontab/CronTask.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index 2e536c3f..bc127bc0 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -2,6 +2,9 @@ namespace App\Crontab; +use Swoft\Crontab\Annotaion\Mapping\Cron; +use Swoft\Crontab\Annotaion\Mapping\Scheduled; + /** * Class CronTask * From f3021296661d6008c2000197337b1b8db7fb9117 Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 3 Aug 2019 23:46:52 +0800 Subject: [PATCH 488/643] update some ws demo --- app/WebSocket/ChatModule.php | 4 +- app/WebSocket/EchoModule.php | 2 +- app/WebSocket/Test/TestController.php | 121 ++++++++++++++++++++++++++ app/WebSocket/TestModule.php | 33 +++++++ app/bean.php | 8 +- 5 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 app/WebSocket/Test/TestController.php create mode 100644 app/WebSocket/TestModule.php diff --git a/app/WebSocket/ChatModule.php b/app/WebSocket/ChatModule.php index 07cd3177..45b79522 100644 --- a/app/WebSocket/ChatModule.php +++ b/app/WebSocket/ChatModule.php @@ -6,7 +6,7 @@ use Swoft\Http\Message\Request; use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen; use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; -use Swoft\WebSocket\Server\MessageParser\TokenTextParser; +use Swoft\WebSocket\Server\MessageParser\JsonParser; use function server; /** @@ -14,7 +14,7 @@ * * @WsModule( * "/chat", - * messageParser=TokenTextParser::class, + * messageParser=JsonParser::class, * controllers={HomeController::class} * ) */ diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php index 0e1dc80a..4aa6ae3f 100644 --- a/app/WebSocket/EchoModule.php +++ b/app/WebSocket/EchoModule.php @@ -13,7 +13,7 @@ /** * Class EchoModule * - * @WsModule() + * @WsModule("echo") */ class EchoModule { diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php new file mode 100644 index 00000000..c83e9856 --- /dev/null +++ b/app/WebSocket/Test/TestController.php @@ -0,0 +1,121 @@ +push('hi, this is test.index'); + } + + /** + * Message command is: 'test.index' + * @param Message $msg + * + * @return void + * @MessageMapping("close") + */ + public function close(Message $msg): void + { + $data = $msg->getData(); + $conn = Session::mustGet(); + + $fd = \is_numeric($data) ? (int)$data : $conn->getFd(); + + $conn->push("hi, will close conn $fd"); + + // disconnect + \server()->disconnect($fd); + } + + /** + * Message command is: 'test.req' + * + * @param Request $req + * + * @return void + * @MessageMapping("req") + */ + public function injectRequest(Request $req): void + { + $fd = $req->getFd(); + + Session::mustGet()->push("(your FD: $fd)message data: " . \json_encode($req->getMessage()->toArray())); + } + + /** + * Message command is: 'test.msg' + * + * @param Message $msg + * + * @return void + * @MessageMapping("msg") + */ + public function injectMessage(Message $msg): void + { + Session::mustGet()->push('message data: ' . \json_encode($msg->toArray())); + } + + /** + * Message command is: 'echo' + * + * @param $data + * @MessageMapping(root=true) + */ + public function echo($data): void + { + Session::mustGet()->push('(echo)Recv: ' . $data); + } + + /** + * Message command is: 'bin' + * + * @param $data + * @MessageMapping("bin", root=true) + */ + public function binary($data): void + { + Session::mustGet()->push('Binary: ' . $data, \WEBSOCKET_OPCODE_BINARY); + } + + /** + * Message command is: 'ping' + * + * @MessageMapping("ping", root=true) + */ + public function pong(): void + { + Session::mustGet()->push('pong!', \WEBSOCKET_OPCODE_PONG); + } + + /** + * Message command is: 'test.ar' + * + * @param $data + * @MessageMapping("ar") + * + * @return string + */ + public function autoReply($data): string + { + return '(home.ar)Recv: ' . $data; + } +} diff --git a/app/WebSocket/TestModule.php b/app/WebSocket/TestModule.php new file mode 100644 index 00000000..10dfde70 --- /dev/null +++ b/app/WebSocket/TestModule.php @@ -0,0 +1,33 @@ +push($request->getFd(), "Opened, welcome!(FD: $fd)"); + } +} diff --git a/app/bean.php b/app/bean.php index ae7d54ca..25f9abd9 100644 --- a/app/bean.php +++ b/app/bean.php @@ -46,6 +46,7 @@ // Add global http middleware 'middlewares' => [ \App\Http\Middleware\FavIconMiddleware::class, + \Swoft\Whoops\WhoopsMiddleware::class, // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, ], @@ -115,7 +116,8 @@ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), ], - 'debug' => env('SWOFT_DEBUG', 0), + 'debug' => 1, + // 'debug' => env('SWOFT_DEBUG', 0), /* @see WebSocketServer::$setting */ 'setting' => [ 'log_file' => alias('@runtime/swoole.log'), @@ -127,8 +129,8 @@ ], /** @see \Swoft\Tcp\Protocol */ 'tcpServerProtocol' => [ - 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, - // 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + // 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, + 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, // 'openLengthCheck' => true, ], 'cliRouter' => [ From 8cb6ba67ecb9010f8cf92ff3fc7b11dee230454d Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 5 Aug 2019 20:55:33 +0800 Subject: [PATCH 489/643] add some demo method for ws server --- app/WebSocket/EchoModule.php | 3 ++- app/WebSocket/Test/TestController.php | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php index 4aa6ae3f..4a72a537 100644 --- a/app/WebSocket/EchoModule.php +++ b/app/WebSocket/EchoModule.php @@ -3,6 +3,7 @@ namespace App\WebSocket; use Swoft\Http\Message\Request; +use Swoft\Session\Session; use Swoft\WebSocket\Server\Annotation\Mapping\OnMessage; use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen; use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; @@ -24,7 +25,7 @@ class EchoModule */ public function onOpen(Request $request, int $fd): void { - server()->push($request->getFd(), "Opened, welcome!(FD: $fd)"); + Session::mustGet()->push("Opened, welcome #{$fd}!"); } /** diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index c83e9856..b160b82a 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -7,6 +7,7 @@ use Swoft\WebSocket\Server\Annotation\Mapping\WsController; use Swoft\WebSocket\Server\Message\Message; use Swoft\WebSocket\Server\Message\Request; +use Swoft\WebSocket\Server\Message\Response; /** * Class HomeController @@ -85,6 +86,26 @@ public function echo($data): void Session::mustGet()->push('(echo)Recv: ' . $data); } + /** + * Message command is: 'echo' + * + * @param Request $req + * @param Response $res + * @MessageMapping(root=true) + */ + public function hi(Request $req, Response $res): void + { + $fd = $req->getFd(); + $ufd = (int)$req->getMessage()->getData(); + + if ($ufd < 1) { + Session::mustGet()->push('data must be an integer'); + return; + } + + $res->setFd($ufd)->setContent("Hi #{$ufd}, I am #{$fd}"); + } + /** * Message command is: 'bin' * From 4740e3bcfadcef3b2d71deacc5b15eec90088cba Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 5 Aug 2019 21:02:00 +0800 Subject: [PATCH 490/643] update demo --- app/WebSocket/TestModule.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/WebSocket/TestModule.php b/app/WebSocket/TestModule.php index 10dfde70..4a1a718f 100644 --- a/app/WebSocket/TestModule.php +++ b/app/WebSocket/TestModule.php @@ -4,10 +4,10 @@ use App\WebSocket\Test\TestController; use Swoft\Http\Message\Request; +use Swoft\Session\Session; use Swoft\WebSocket\Server\Annotation\Mapping\OnOpen; use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; use Swoft\WebSocket\Server\MessageParser\TokenTextParser; -use function server; /** * Class TestModule @@ -28,6 +28,6 @@ class TestModule */ public function onOpen(Request $request, int $fd): void { - server()->push($request->getFd(), "Opened, welcome!(FD: $fd)"); + Session::mustGet()->push("Opened, welcome!(FD: $fd)"); } } From 190a1a5d0a0992af73aabfab5152b9e71bcb0fd0 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 6 Aug 2019 20:18:04 +0800 Subject: [PATCH 491/643] Add crontab demo --- app/Crontab/CronTask.php | 17 +++++++++++++++-- app/bean.php | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index bc127bc0..b6eafc7a 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -2,8 +2,11 @@ namespace App\Crontab; +use App\Model\Entity\User; use Swoft\Crontab\Annotaion\Mapping\Cron; use Swoft\Crontab\Annotaion\Mapping\Scheduled; +use Swoft\Log\Helper\CLog; +use Swoft\Stdlib\Helper\JsonHelper; /** * Class CronTask @@ -19,7 +22,17 @@ class CronTask */ public function secondTask() { - printf("second task run: %s ", date('Y-m-d H:i:s', time())); + $user = new User(); + $user->setAge(mt_rand(1, 100)); + $user->setUserDesc('desc'); + + $user->save(); + + $id = $user->getId(); + $user = User::find($id)->toArray(); + + CLog::info("second task run: %s ", date('Y-m-d H:i:s', time())); + CLog::info(JsonHelper::encode($user)); } /** @@ -27,7 +40,7 @@ public function secondTask() */ public function minuteTask() { - printf("minute task run: %s ", date('Y-m-d H:i:s', time())); + CLog::info("minute task run: %s ", date('Y-m-d H:i:s', time())); } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index 80a50f54..22a631dd 100644 --- a/app/bean.php +++ b/app/bean.php @@ -2,6 +2,7 @@ use App\Common\DbSelector; use App\Process\MonitorProcess; +use Swoft\Crontab\Process\CrontabProcess; use Swoft\Db\Pool; use Swoft\Http\Server\HttpServer; use Swoft\Task\Swoole\SyncTaskListener; @@ -15,7 +16,6 @@ use Swoft\Server\SwooleEvent; use Swoft\Db\Database; use Swoft\Redis\RedisDb; -use Swoft\Crontab\Crontab; return [ 'logger' => [ @@ -31,7 +31,7 @@ ], 'process' => [ // 'monitor' => bean(MonitorProcess::class) -// 'crontab' => bean(Crontab::class) +// 'crontab' => bean(CrontabProcess::class) ], 'on' => [ // SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task From 67486cb4e4b7ba415dcb6357ee989f22325e3e67 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 6 Aug 2019 21:33:02 +0800 Subject: [PATCH 492/643] Modify worker num --- app/bean.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/bean.php b/app/bean.php index 22a631dd..b111ecca 100644 --- a/app/bean.php +++ b/app/bean.php @@ -41,12 +41,13 @@ /* @see HttpServer::$setting */ 'setting' => [ 'task_worker_num' => 12, - 'task_enable_coroutine' => true + 'task_enable_coroutine' => true, + 'worker_num' => 6, ] ], 'httpDispatcher' => [ // Add global http middleware - 'middlewares' => [ + 'middlewares' => [ \App\Http\Middleware\FavIconMiddleware::class, // \Swoft\Whoops\WhoopsMiddleware::class, // Allow use @View tag @@ -135,7 +136,7 @@ /** @see \Swoft\Tcp\Protocol */ 'tcpServerProtocol' => [ // 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, - 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, + 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, // 'openLengthCheck' => true, ], 'cliRouter' => [ From 53f3ca3fb0ed8aafd4dce0d62e67a3f46f52ebbc Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 6 Aug 2019 21:51:21 +0800 Subject: [PATCH 493/643] add --- app/bean.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/bean.php b/app/bean.php index b111ecca..1213fa6c 100644 --- a/app/bean.php +++ b/app/bean.php @@ -42,7 +42,7 @@ 'setting' => [ 'task_worker_num' => 12, 'task_enable_coroutine' => true, - 'worker_num' => 6, + 'worker_num' => 2, ] ], 'httpDispatcher' => [ From a380dfa6a8c9666300b09d387fb6cbe0d25a0793 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 6 Aug 2019 21:51:59 +0800 Subject: [PATCH 494/643] add --- app/bean.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/bean.php b/app/bean.php index 1213fa6c..726c20f9 100644 --- a/app/bean.php +++ b/app/bean.php @@ -41,8 +41,7 @@ /* @see HttpServer::$setting */ 'setting' => [ 'task_worker_num' => 12, - 'task_enable_coroutine' => true, - 'worker_num' => 2, + 'task_enable_coroutine' => true ] ], 'httpDispatcher' => [ From 8bc9908768651a8e78f0ba90941203315a20e0fc Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 6 Aug 2019 22:45:03 +0800 Subject: [PATCH 495/643] add --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 2fc424de..86e33f9d 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "swoft/consul": "~2.0.0", "swoft/limiter": "~2.0.0", "swoft/breaker": "~2.0.0", + "swoft/crontab": "~2.0.0", "swoft/devtool": "~2.0.0" }, "require-dev": { From b34f2c08e8016dc53f119c54854c5d86430b90c4 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 6 Aug 2019 22:54:16 +0800 Subject: [PATCH 496/643] upgrade swoole to 4.4.3 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1b210d38..191cb06e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.4.2 \ + SWOOLE_VERSION=4.4.3 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From 7036058e89fe80a64ab2a3daac27098d55355bd9 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 7 Aug 2019 12:22:28 +0800 Subject: [PATCH 497/643] update: install some tools for docker --- Dockerfile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 191cb06e..2997bc70 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV APP_ENV=${app_env:-"prod"} \ # Libs -y --no-install-recommends RUN apt-get update \ && apt-get install -y \ - curl wget git zip unzip less vim openssl \ + curl wget git zip unzip less vim procps losf tcpdump htop openssl \ libz-dev \ libssl-dev \ libnghttp2-dev \ @@ -37,13 +37,14 @@ RUN apt-get update \ libjpeg-dev \ libpng-dev \ libfreetype6-dev \ +# Install PHP extensions + && docker-php-ext-install \ + bcmath gd pdo_mysql mbstring sockets zip sysvmsg sysvsem sysvshm + # Install composer - && curl -sS https://getcomposer.org/installer | php \ +Run curl -sS https://getcomposer.org/installer | php \ && mv composer.phar /usr/local/bin/composer \ && composer self-update --clean-backups \ -# Install PHP extensions - && docker-php-ext-install \ - bcmath gd pdo_mysql mbstring sockets zip sysvmsg sysvsem sysvshm \ # Install redis extension && wget http://pecl.php.net/get/redis-${PHPREDIS_VERSION}.tgz -O /tmp/redis.tar.tgz \ && pecl install /tmp/redis.tar.tgz \ From 510ccc5b4aeab4698f4028d159b622f0d921e4c1 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 7 Aug 2019 13:31:58 +0800 Subject: [PATCH 498/643] fix: tool name is error --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 2997bc70..cc9ce642 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ ENV APP_ENV=${app_env:-"prod"} \ # Libs -y --no-install-recommends RUN apt-get update \ && apt-get install -y \ - curl wget git zip unzip less vim procps losf tcpdump htop openssl \ + curl wget git zip unzip less vim procps lsof tcpdump htop openssl \ libz-dev \ libssl-dev \ libnghttp2-dev \ From f1272b312fa230edd55a2767d4a84711898df56e Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 7 Aug 2019 20:01:19 +0800 Subject: [PATCH 499/643] Update docker-compose.yml --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 0149d563..6b795d76 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,8 @@ version: '3.4' services: swoft: image: swoft/swoft +# for local develop +# command: php -S 127.0.0.1:13300 container_name: swoft-srv environment: - APP_ENV=dev From 988ba946754dae194f8448e5d1cabef66bfca957 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 8 Aug 2019 21:54:15 +0800 Subject: [PATCH 500/643] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 375286a2..f9dd258f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw ## Feature -- Built-in high performance network server(Http/Websocket/RPC) +- Built-in high performance network server(Http/Websocket/RPC/TCP) - Flexible componentization - Flexible annotation function - Diversified command terminal(Console) @@ -43,6 +43,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Database is highly compatible Laravel - Cache Redis highly compatible Laravel - Efficient task processing +- Efficient seconds corntab - Flexible exception handling - Powerful log system - Service registration & discovery From 9fad9aa97659ede5b088693327cca3ee2956d684 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 8 Aug 2019 22:20:02 +0800 Subject: [PATCH 501/643] Update README.md --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/README.md b/README.md index f9dd258f..1fc24314 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,44 @@ composer create-project swoft/swoft swoft [root@swoft swoft]# php bin/swoft rpc:start ``` +## Components + +## Core Components + +Component Name | Packagist Version +--------------------|--------------------- +annotation | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/annotation.svg)](https://packagist.org/packages/swoft/annotation) +config | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/config.svg)](https://packagist.org/packages/swoft/config) +db | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/db.svg)](https://packagist.org/packages/swoft/db) +framework | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/framework.svg)](https://packagist.org/packages/swoft/framework) +i18n | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/i18n.svg)](https://packagist.org/packages/swoft/i18n) +proxy | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/proxy.svg)](https://packagist.org/packages/swoft/proxy) +rpc-client | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-client.svg)](https://packagist.org/packages/swoft/rpc-client) +stdlib | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/stdlib.svg)](https://packagist.org/packages/swoft/stdlib) +tcp-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp-server.svg)](https://packagist.org/packages/swoft/tcp-server) +aop | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/aop.svg)](https://packagist.org/packages/swoft/aop) +connection-pool | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/connection-pool.svg)](https://packagist.org/packages/swoft/connection-pool) +error | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/error.svg)](https://packagist.org/packages/swoft/error) +http-message | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-message.svg)](https://packagist.org/packages/swoft/http-message) +log | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/log.svg)](https://packagist.org/packages/swoft/log) +redis | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/redis.svg)](https://packagist.org/packages/swoft/redis) +rpc-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-server.svg)](https://packagist.org/packages/swoft/rpc-server) +task | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/task.svg)](https://packagist.org/packages/swoft/task) +validator | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/validator.svg)](https://packagist.org/packages/swoft/validator) +bean | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/bean.svg)](https://packagist.org/packages/swoft/bean) +console | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/console.svg)](https://packagist.org/packages/swoft/console) +event | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/event.svg)](https://packagist.org/packages/swoft/event) +http-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-server.svg)](https://packagist.org/packages/swoft/http-server) +process | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/process.svg)](https://packagist.org/packages/swoft/process) +rpc | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc.svg)](https://packagist.org/packages/swoft/rpc) +server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/server.svg)](https://packagist.org/packages/swoft/server) +tcp | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/tcp) +websocket-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/websocket-server.svg)](https://packagist.org/packages/swoft/websocket-server) + +### Extension Components + + + ## License Swoft is an open-source software licensed under the [LICENSE](LICENSE) From 973e9c154bee372daefd69ec20c950caaa17e0f0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 8 Aug 2019 22:23:56 +0800 Subject: [PATCH 502/643] Update README.md --- README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1fc24314..b425f3aa 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,6 @@ composer create-project swoft/swoft swoft [root@swoft swoft]# php bin/swoft rpc:start ``` -## Components - ## Core Components Component Name | Packagist Version @@ -133,9 +131,17 @@ server | [![Latest Stable Version](http://img.shields.io/packagis tcp | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/tcp) websocket-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/websocket-server.svg)](https://packagist.org/packages/swoft/websocket-server) -### Extension Components - +## Extension Components +Component Name | Packagist Version +-----------------|--------------------- +apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/apollo) +breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/breaker) +crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/crontab) +consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/consul) +limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/limiter) +view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/view) +whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/whoops) ## License From bd4cb56d0b52c979f7cf362357e4b09c3c66bb33 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 8 Aug 2019 22:25:49 +0800 Subject: [PATCH 503/643] Update README.md --- README.md | 68 +++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index b425f3aa..6daf4855 100644 --- a/README.md +++ b/README.md @@ -103,45 +103,45 @@ composer create-project swoft/swoft swoft Component Name | Packagist Version --------------------|--------------------- -annotation | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/annotation.svg)](https://packagist.org/packages/swoft/annotation) -config | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/config.svg)](https://packagist.org/packages/swoft/config) -db | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/db.svg)](https://packagist.org/packages/swoft/db) -framework | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/framework.svg)](https://packagist.org/packages/swoft/framework) -i18n | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/i18n.svg)](https://packagist.org/packages/swoft/i18n) -proxy | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/proxy.svg)](https://packagist.org/packages/swoft/proxy) -rpc-client | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-client.svg)](https://packagist.org/packages/swoft/rpc-client) -stdlib | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/stdlib.svg)](https://packagist.org/packages/swoft/stdlib) -tcp-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp-server.svg)](https://packagist.org/packages/swoft/tcp-server) -aop | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/aop.svg)](https://packagist.org/packages/swoft/aop) -connection-pool | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/connection-pool.svg)](https://packagist.org/packages/swoft/connection-pool) -error | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/error.svg)](https://packagist.org/packages/swoft/error) -http-message | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-message.svg)](https://packagist.org/packages/swoft/http-message) -log | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/log.svg)](https://packagist.org/packages/swoft/log) -redis | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/redis.svg)](https://packagist.org/packages/swoft/redis) -rpc-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-server.svg)](https://packagist.org/packages/swoft/rpc-server) -task | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/task.svg)](https://packagist.org/packages/swoft/task) -validator | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/validator.svg)](https://packagist.org/packages/swoft/validator) -bean | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/bean.svg)](https://packagist.org/packages/swoft/bean) -console | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/console.svg)](https://packagist.org/packages/swoft/console) -event | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/event.svg)](https://packagist.org/packages/swoft/event) -http-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-server.svg)](https://packagist.org/packages/swoft/http-server) -process | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/process.svg)](https://packagist.org/packages/swoft/process) -rpc | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc.svg)](https://packagist.org/packages/swoft/rpc) -server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/server.svg)](https://packagist.org/packages/swoft/server) -tcp | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/tcp) -websocket-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/websocket-server.svg)](https://packagist.org/packages/swoft/websocket-server) +swoft-annotation | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/annotation.svg)](https://packagist.org/packages/swoft/annotation) +swoft-config | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/config.svg)](https://packagist.org/packages/swoft/config) +swoft-db | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/db.svg)](https://packagist.org/packages/swoft/db) +swoft-framework | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/framework.svg)](https://packagist.org/packages/swoft/framework) +swoft-i18n | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/i18n.svg)](https://packagist.org/packages/swoft/i18n) +swoft-proxy | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/proxy.svg)](https://packagist.org/packages/swoft/proxy) +swoft-rpc-client | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-client.svg)](https://packagist.org/packages/swoft/rpc-client) +swoft-stdlib | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/stdlib.svg)](https://packagist.org/packages/swoft/stdlib) +swoft-tcp-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp-server.svg)](https://packagist.org/packages/swoft/tcp-server) +swoft-aop | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/aop.svg)](https://packagist.org/packages/swoft/aop) +swoft-connection-pool | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/connection-pool.svg)](https://packagist.org/packages/swoft/connection-pool) +swoft-error | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/error.svg)](https://packagist.org/packages/swoft/error) +swoft-http-message | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-message.svg)](https://packagist.org/packages/swoft/http-message) +swoft-log | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/log.svg)](https://packagist.org/packages/swoft/log) +swoft-redis | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/redis.svg)](https://packagist.org/packages/swoft/redis) +swoft-rpc-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-server.svg)](https://packagist.org/packages/swoft/rpc-server) +swoft-task | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/task.svg)](https://packagist.org/packages/swoft/task) +swoft-validator | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/validator.svg)](https://packagist.org/packages/swoft/validator) +swoft-bean | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/bean.svg)](https://packagist.org/packages/swoft/bean) +swoft-console | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/console.svg)](https://packagist.org/packages/swoft/console) +swoft-event | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/event.svg)](https://packagist.org/packages/swoft/event) +swoft-http-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-server.svg)](https://packagist.org/packages/swoft/http-server) +swoft-process | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/process.svg)](https://packagist.org/packages/swoft/process) +swoft-rpc | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc.svg)](https://packagist.org/packages/swoft/rpc) +swoft-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/server.svg)](https://packagist.org/packages/swoft/server) +swoft-tcp | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/tcp) +swoft-websocket-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/websocket-server.svg)](https://packagist.org/packages/swoft/websocket-server) ## Extension Components Component Name | Packagist Version -----------------|--------------------- -apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/apollo) -breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/breaker) -crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/crontab) -consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/consul) -limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/limiter) -view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/view) -whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/whoops) +swoft-apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/apollo) +swoft-breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/breaker) +swoft-crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/crontab) +swoft-consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/consul) +swoft-limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/limiter) +swoft-view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/view) +swoft-whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/whoops) ## License From aa5af828ebf3a5da83c61c167f545445c2546894 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 8 Aug 2019 22:27:18 +0800 Subject: [PATCH 504/643] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6daf4855..1f815dd9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) -[![Docker Build Status](https://img.shields.io/docker/build/swoft/swoft.svg)](https://hub.docker.com/r/swoft/swoft/) +[![Docker Build Status](https://img.shields.io/docker/build/swoft/alphp.svg)](https://hub.docker.com/r/swoft/swoft/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.4.1-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) [![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) @@ -135,13 +135,13 @@ swoft-websocket-server | [![Latest Stable Version](http://img.shields.io/pa Component Name | Packagist Version -----------------|--------------------- -swoft-apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/apollo) -swoft-breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/breaker) -swoft-crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/crontab) -swoft-consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/consul) -swoft-limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/limiter) -swoft-view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/view) -swoft-whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/whoops) +swoft-apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/apollo.svg)](https://packagist.org/packages/swoft/apollo) +swoft-breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/breaker.svg)](https://packagist.org/packages/swoft/breaker) +swoft-crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/crontab.svg)](https://packagist.org/packages/swoft/crontab) +swoft-consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/consul.svg)](https://packagist.org/packages/swoft/consul) +swoft-limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/limiter.svg)](https://packagist.org/packages/swoft/limiter) +swoft-view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/view.svg)](https://packagist.org/packages/swoft/view) +swoft-whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/whoops.svg)](https://packagist.org/packages/swoft/whoops) ## License From 8ec6d568dbb5a5e88fe14fb649cf176080ff54b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Fri, 9 Aug 2019 08:47:15 +0800 Subject: [PATCH 505/643] Update README.zh-CN.md --- README.zh-CN.md | 52 +++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index b174d3e6..4a5eb12a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -27,31 +27,33 @@ Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP ## 功能特色 - - 内置高性能网络服务器(Http/Websocket/RPC) - - 灵活的组件功能 - - 强大的注解功能 - - 多样化的命令终端(控制台) - - 强大的面向切面编程(AOP) - - 容器管理,依赖注入(DI) - - 灵活的事件机制 - - 基于PSR-7的HTTP消息的实现 - - 基于PSR-14的事件管理 - - 基于PSR-15的中间件 - - 国际化(i18n)支持 - - 简单有效的参数验证器 - - 高性能连接池(Mysql/Redis/RPC),自动重新连接 - - 数据库高度兼容Laravel的使用方式 - - Redis高度兼容Laravel的使用方式 - - 高效的任务处理 - - 灵活的异常处理 - - 强大的日志系统 - - 服务注册与发现 - - 配置中心 - - 服务限流 - - 服务降级 - - 服务熔断 - - Apollo - - Consul + - 内置高性能网络服务器(Http/Websocket/RPC/TCP) +- 灵活的组件功能 +- 强大的注解功能 +- 多样化的命令终端(控制台) +- 强大的面向切面编程(AOP) +- 容器管理,依赖注入(DI) +- 灵活的事件机制 +- 基于PSR-7的HTTP消息的实现 +- 基于PSR-14的事件管理 +- 基于PSR-15的中间件 +- 国际化(i18n)支持 +- 简单有效的参数验证器 +- 高性能连接池(Mysql/Redis/RPC),自动重新连接 +- 数据库高度兼容Laravel的使用方式 +- Redis高度兼容Laravel的使用方式 +- 秒级定时任务 +- 进程池 +- 高效的任务处理 +- 灵活的异常处理 +- 强大的日志系统 +- 服务注册与发现 +- 配置中心 +- 服务限流 +- 服务降级 +- 服务熔断 +- Apollo +- Consul ## 在线文档 From 79b05046c0b5da886b887c86c7fbdee2104de066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Fri, 9 Aug 2019 08:49:02 +0800 Subject: [PATCH 506/643] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f815dd9..8c74824e 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Cache Redis highly compatible Laravel - Efficient task processing - Efficient seconds corntab +- Process pool - Flexible exception handling - Powerful log system - Service registration & discovery From 62b9490d283eaa33875cec5806eaa2d80a95e797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Fri, 9 Aug 2019 08:51:39 +0800 Subject: [PATCH 507/643] Update README.md --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 8c74824e..214f760f 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,18 @@ composer create-project swoft/swoft swoft [root@swoft swoft]# php bin/swoft rpc:start ``` +- TCP server + +```bash +[root@swoft swoft]# php bin/swoft tcp:start +``` + +- Process pool + +```bash +[root@swoft swoft]# php bin/swoft process:start +``` + ## Core Components Component Name | Packagist Version From 0f5dae493753f4b8fa78c72274eeb30d828397ae Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 9 Aug 2019 10:37:02 +0800 Subject: [PATCH 508/643] up: remove ./vendor/bin/phpstan analyze --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 86e33f9d..7a1d5d46 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,6 @@ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "test": [ - "./vendor/bin/phpstan analyze", "./vendor/bin/phpunit -c phpunit.xml" ], "cs-fix": "./vendor/bin/php-cs-fixer fix $1" From 70decf2dbe3627c2d87f87e80b8d5a5ad642fe7a Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 9 Aug 2019 20:29:59 +0800 Subject: [PATCH 509/643] add log file format --- app/Crontab/CronTask.php | 4 ++++ app/bean.php | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index b6eafc7a..dced6283 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -6,6 +6,7 @@ use Swoft\Crontab\Annotaion\Mapping\Cron; use Swoft\Crontab\Annotaion\Mapping\Scheduled; use Swoft\Log\Helper\CLog; +use Swoft\Log\Helper\Log; use Swoft\Stdlib\Helper\JsonHelper; /** @@ -28,9 +29,12 @@ public function secondTask() $user->save(); + Log::profileStart("name"); $id = $user->getId(); $user = User::find($id)->toArray(); + Log::profileEnd("name"); + CLog::info("second task run: %s ", date('Y-m-d H:i:s', time())); CLog::info(JsonHelper::encode($user)); } diff --git a/app/bean.php b/app/bean.php index 726c20f9..b0040253 100644 --- a/app/bean.php +++ b/app/bean.php @@ -18,9 +18,15 @@ use Swoft\Redis\RedisDb; return [ + 'noticeHandler' => [ + 'logFile' => '@runtime/logs/notice-%d{Y-m-d-H}.log', + ], + 'applicationHandler' => [ + 'logFile' => '@runtime/logs/error-%d{Y-m-d}.log', + ], 'logger' => [ - 'flushRequest' => false, - 'enable' => false, + 'flushRequest' => true, + 'enable' => true, 'json' => false, ], 'httpServer' => [ From 18e82e1ad26858ca936e2f97ff2da436a5199c06 Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 10 Aug 2019 11:19:32 +0800 Subject: [PATCH 510/643] update some demo class --- app/Application.php | 14 ++++++++++++++ app/WebSocket/Test/TestController.php | 22 ++++++++++++++-------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/Application.php b/app/Application.php index 43f71606..25b41540 100644 --- a/app/Application.php +++ b/app/Application.php @@ -16,6 +16,20 @@ protected function beforeInit(): void { parent::beforeInit(); + // you can init php setting. date_default_timezone_set('Asia/Shanghai'); } + + /** + * @return array + */ + public function getCLoggerConfig(): array + { + $config = parent::getCLoggerConfig(); + + // False: Dont print log to terminal + $config['enable'] = true; + + return $config; + } } diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index b160b82a..01f274b0 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -8,6 +8,9 @@ use Swoft\WebSocket\Server\Message\Message; use Swoft\WebSocket\Server\Message\Request; use Swoft\WebSocket\Server\Message\Response; +use function is_numeric; +use function json_encode; +use const WEBSOCKET_OPCODE_PONG; /** * Class HomeController @@ -39,12 +42,12 @@ public function close(Message $msg): void $data = $msg->getData(); $conn = Session::mustGet(); - $fd = \is_numeric($data) ? (int)$data : $conn->getFd(); + $fd = is_numeric($data) ? (int)$data : $conn->getFd(); $conn->push("hi, will close conn $fd"); // disconnect - \server()->disconnect($fd); + $conn->getServer()->disconnect($fd); } /** @@ -59,7 +62,7 @@ public function injectRequest(Request $req): void { $fd = $req->getFd(); - Session::mustGet()->push("(your FD: $fd)message data: " . \json_encode($req->getMessage()->toArray())); + Session::mustGet()->push("(your FD: $fd)message data: " . json_encode($req->getMessage()->toArray())); } /** @@ -72,7 +75,7 @@ public function injectRequest(Request $req): void */ public function injectMessage(Message $msg): void { - Session::mustGet()->push('message data: ' . \json_encode($msg->toArray())); + Session::mustGet()->push('message data: ' . json_encode($msg->toArray())); } /** @@ -110,11 +113,14 @@ public function hi(Request $req, Response $res): void * Message command is: 'bin' * * @param $data - * @MessageMapping("bin", root=true) + * @MessageMapping("bin", root=true, opcode=2) + * + * @return string */ - public function binary($data): void + public function binary($data): string { - Session::mustGet()->push('Binary: ' . $data, \WEBSOCKET_OPCODE_BINARY); + // Session::mustGet()->push('Binary: ' . $data, \WEBSOCKET_OPCODE_BINARY); + return 'Binary: ' . $data; } /** @@ -124,7 +130,7 @@ public function binary($data): void */ public function pong(): void { - Session::mustGet()->push('pong!', \WEBSOCKET_OPCODE_PONG); + Session::mustGet()->push('pong!', WEBSOCKET_OPCODE_PONG); } /** From 8262f5c1933c956242bb7dbc71ce96a08c9930d5 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Tue, 13 Aug 2019 19:11:31 +0800 Subject: [PATCH 511/643] add log --- app/Crontab/CronTask.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index dced6283..84dc982b 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -35,6 +35,8 @@ public function secondTask() Log::profileEnd("name"); + Log::info("info message", ['a' => 'b']); + CLog::info("second task run: %s ", date('Y-m-d H:i:s', time())); CLog::info(JsonHelper::encode($user)); } From 0b44b13711ed387c7f76d28ebacc7751aa97b8b5 Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 14 Aug 2019 10:33:26 +0800 Subject: [PATCH 512/643] up some --- app/Crontab/CronTask.php | 33 +++++++++++++++------------------ test/bootstrap.php | 2 +- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index b6eafc7a..d90ce621 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -2,11 +2,9 @@ namespace App\Crontab; -use App\Model\Entity\User; use Swoft\Crontab\Annotaion\Mapping\Cron; use Swoft\Crontab\Annotaion\Mapping\Scheduled; use Swoft\Log\Helper\CLog; -use Swoft\Stdlib\Helper\JsonHelper; /** * Class CronTask @@ -20,27 +18,26 @@ class CronTask /** * @Cron("* * * * * *") */ - public function secondTask() + public function secondTask(): void { - $user = new User(); - $user->setAge(mt_rand(1, 100)); - $user->setUserDesc('desc'); - - $user->save(); - - $id = $user->getId(); - $user = User::find($id)->toArray(); - - CLog::info("second task run: %s ", date('Y-m-d H:i:s', time())); - CLog::info(JsonHelper::encode($user)); + // $user = new User(); + // $user->setAge(mt_rand(1, 100)); + // $user->setUserDesc('desc'); + // + // $user->save(); + // + // $id = $user->getId(); + // $user = User::find($id)->toArray(); + + CLog::info('second task run: %s ', date('Y-m-d H:i:s', time())); + // CLog::info(JsonHelper::encode($user)); } /** * @Cron("0 * * * * *") */ - public function minuteTask() + public function minuteTask(): void { - CLog::info("minute task run: %s ", date('Y-m-d H:i:s', time())); + CLog::info('minute task run: %s ', date('Y-m-d H:i:s', time())); } - -} \ No newline at end of file +} diff --git a/test/bootstrap.php b/test/bootstrap.php index ba7b26ad..a44b83c5 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,3 +1,3 @@ Date: Wed, 14 Aug 2019 12:36:48 +0800 Subject: [PATCH 513/643] Update CODE_OF_CONDUCT.md --- .github/CODE_OF_CONDUCT.md | 94 +++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index c2a23273..9f1ab7ba 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,12 +1,92 @@ -Contributor Code of Conduct -As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. +# Contributing -We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. +When contributing to this repository, please first discuss the change you wish to make via issue, +email, or any other method with the owners of this repository before making a change. -Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. +Please note we have a code of conduct, please follow it in all your interactions with the project. -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. +## Pull Request Process -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. +1. Ensure any install or build dependencies are removed before the end of the layer when doing a + build. +2. Update the README.md with details of changes to the interface, this includes new environment + variables, exposed ports, useful file locations and container parameters. +3. Increase the version numbers in any examples files and the README.md to the new version that this + Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. -This Code of Conduct is adapted from the Contributor Covenant, version 1.0.0, available at http://contributor-covenant.org/version/1/0/0/ \ No newline at end of file +## Code of Conduct + +### Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +### Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +### Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ From b02571d31efb6d0bd1f427155ac16dacf7021090 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 14 Aug 2019 12:38:30 +0800 Subject: [PATCH 514/643] Update CODE_OF_CONDUCT.md --- .github/CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 9f1ab7ba..a9668046 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -73,7 +73,7 @@ further defined and clarified by project maintainers. ### Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All +reported by contacting the project team at group@swoft.org . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. From f5ad98fb8b6967ff9cf0d28ad6b64a7f59c3b2f6 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 14 Aug 2019 20:03:58 +0800 Subject: [PATCH 515/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 214f760f..ffba7b46 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw ## Document -- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) +- [中文文档](https://www.swoft.org/docs) - [English](https://en.swoft.org/docs) ## Discuss From c59795e2327ede2dce16d1f0130fb04ca8c7fbb4 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 18 Aug 2019 11:49:39 +0800 Subject: [PATCH 516/643] fix: #903 env data format error on startup by docker compose --- .env.example | 7 +++++-- app/Console/Command/AgentCommand.php | 26 +++++++++--------------- app/Listener/RegisterServiceListener.php | 3 +-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/.env.example b/.env.example index 7f3f9910..9ef091fa 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,5 @@ -APP_DEBUG = 0 -SWOFT_DEBUG = 0 +# basic +APP_DEBUG=0 +SWOFT_DEBUG=0 + +# more ... diff --git a/app/Console/Command/AgentCommand.php b/app/Console/Command/AgentCommand.php index 2e9819e7..b2d423a8 100644 --- a/app/Console/Command/AgentCommand.php +++ b/app/Console/Command/AgentCommand.php @@ -1,19 +1,14 @@ restart(); + // /** @var HttpServer $server */ + // $server = bean('httpServer'); + // $server->restart(); -// /** @var ServiceServer $server */ -// $server = bean('rpcServer'); -// $server->restart(); + // /** @var ServiceServer $server */ + // $server = bean('rpcServer'); + // $server->restart(); /** @var WebSocketServer $server */ $server = bean('wsServer'); $server->restart(); } } -} \ No newline at end of file +} diff --git a/app/Listener/RegisterServiceListener.php b/app/Listener/RegisterServiceListener.php index 24c84089..a92ae6c2 100644 --- a/app/Listener/RegisterServiceListener.php +++ b/app/Listener/RegisterServiceListener.php @@ -5,7 +5,6 @@ use Swoft\Bean\Annotation\Mapping\Inject; -use Swoft\Co; use Swoft\Consul\Agent; use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; @@ -63,4 +62,4 @@ public function handle(EventInterface $event): void // CLog::info('Swoft http register service success by consul!'); } -} \ No newline at end of file +} From 9a66f83344819af91433109d61c2def8421764f5 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 18 Aug 2019 16:45:06 +0800 Subject: [PATCH 517/643] Delete start-http-server.jpg --- public/image/start-http-server.jpg | Bin 74998 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 public/image/start-http-server.jpg diff --git a/public/image/start-http-server.jpg b/public/image/start-http-server.jpg deleted file mode 100644 index 1e38f23a56b15db764c9de70b55e3d08780faa1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74998 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 From 9eecbbaab4c73d93ff2b2b79fdd3e8fedd9eb9ee Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 18 Aug 2019 16:46:01 +0800 Subject: [PATCH 518/643] up: upload new start-http-server.jpg --- public/start-http-server.jpg | Bin 0 -> 123829 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/start-http-server.jpg diff --git a/public/start-http-server.jpg b/public/start-http-server.jpg new file mode 100644 index 0000000000000000000000000000000000000000..baa3b2b89e6a108918e626b6e8b68a31785812ae GIT binary patch literal 123829 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[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
        o(f!JOi#*7uSUWS6ZT!$GlFd7ZgBFGokh5nRW+R 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 f4mQny!-0-msDK))b5Yd!KoX* Date: Sun, 18 Aug 2019 16:47:41 +0800 Subject: [PATCH 519/643] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ffba7b46..f9e1b2fc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) [![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) -![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/start-http-server.jpg) +![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/start-http-server.jpg) PHP microservices coroutine framework From 8c7ac3a6691f73f1532935345b85b4068bfcd5d8 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 18 Aug 2019 16:51:55 +0800 Subject: [PATCH 520/643] update image on readme --- README.md | 2 +- public/image/start-http-server.jpg | Bin 0 -> 113531 bytes public/start-http-server.jpg | Bin 123829 -> 0 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 public/image/start-http-server.jpg delete mode 100644 public/start-http-server.jpg diff --git a/README.md b/README.md index f9e1b2fc..ffba7b46 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) [![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) -![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/start-http-server.jpg) +![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/start-http-server.jpg) PHP microservices coroutine framework diff --git a/public/image/start-http-server.jpg b/public/image/start-http-server.jpg new file mode 100644 index 0000000000000000000000000000000000000000..19810bb920954b12d84f844c0f7802d1d0a72c73 GIT binary patch literal 113531 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

        #&PeMLkLP}j0iSz6>VZ(AThW95TQ}Js~cY;Fn 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

        mu!%J@+^?eC_C{w>QVilBPjSO zzkD~>P=gKVU_v;N4AI!h<&z`}lDx%LI(sQ$&GM22;ii5GA7*>!-o!d};y{GbyS z#94QVkt@1|fI99c%3`uKw5R=<*nxbPX-G7rViW>p+gvNaiOO7>Pwt<3uSk$@g;bsv znlRf5W|y0j4bAtB-u9Jj0+Y^MJ&jg8)85BEKPJ|-M!}1gck0Awy44IJvkU+5+BFw3 zFJB)mV@&YwTv1TmCLL^q(-s>=KA+Ole5!KG)mN01I>TX+{JuL1KrY={txvx)uxKT$ z9iVo%+eB0Vq+^_1*+7YtG{)vqsCV_~jA=e+FMCqXsP*M355602=D{#BY9&gVF=C;x zA*(!O)$88bV(Sg#vtWQ;vh~WAv@1$V26;Uq11;5EeSjM-a>bnbmIyO=elj3D;EJ{f z2QCXJmLt!xM{nGE?c6~jM2pxUsJ)NUD$M5#4sJPpvVS!2xb=~^`eUU`KL&4G7$9}q zs-Et=IbReYOiGx-L@} za~kVOoKM?cZKja;5L)5&(n`C;Vji2(_--|M3sVh6waGn!3&~6P^<;^0q)lz!H>izq zdT9Jc?;B_z|LIVHFt)SjH&8SG^-NBhtOvcAjV-$Eo!QgG@q6iq6=a!V0I@WdpD@di zyAg`_a+&0)^%IyaWgg5=sd<%>c8)B&E^bU1>q@ltjBdUUWpcgiqV(O>VGn-lJv8DE zGRdtcsWrR+l^y;(xH%DvRQO9CB)-oe(;RQsqS9I2z2#mD63K}K<=hb>$x5#Cq|BKv zBztn^3`Mi1w!$Lh^`xG7=?HfeCHkjN5)y2kD^;_;7c{N1az@&fXKtMLH~6Kcn72*s zulG!CVaVqM439deDmofhYS9*k1niYk;YARf-oxT1}DhvSM6Tx zVNog|rTVIoV*-V@I{h0?P{s?9E4imn(nPb!UHmW4_}V;>+!q(DSelqVO=!3a*HjM= zF42?2+KTIwdP-GAH?VLw>OtalS&&8WU9|Vm-Q#Jdxl-)1Ru|UIkW?oW#3bXzpg9(I zrUjO_b?p5+?%L+XocQ-<=raSE&*o!rLNkvi`xm^0OzP0xdHRnTSWp02=Xl71zyQHx zIB4{gx(bK}OD63BHG3MJHAlPUNzofdtQK-YY`bI{ z#Vao_T<7oVUFc$qEQA)G1Xant956VINaBogw+B4zy22YNfz0pMb(+KU% z=ae4Rfsu$L+lxMszWn|=I%npw1~QK7z1rmN>DHH#tWl!4;U5U>B6t_MR$&NSFcI_0 zAguGXjr!ApR@W)JDM_fbbr{o8^=+xHIfD-k4cA`1kdE!;OQ9C!H>pg%o|0UfS|RvE zr6ZxOAH=eeHJ2k;2~BSHP7ulavOL||^7vtkYF(J@>K1z`2_dr784E75awlyvy9HvYtYo@38s9m^tTdl@mWyfFD+U};@E$21~R*{g7W>N#k8rK0R4oQm+FEmV-1 z_}(lm$%~+sPbal`vcTTzsoEPSoji??yq!n^Bwn*m51`4O6IpvRo9nkvN5#=YKwTg1 zoI9TwoSWu3v+pC7F8jQL_y!uN^P+F?UIid$499t}G!MZyO&$mn6B5=;=VXpZab5K?QRV2V;xZgros-~#Ay{x` zL*Ec9~JTZvgYr@ zj2xR%71C%$!Wiax=~*^WA>h}8vKeyPiI+WxJRUCxMMUN_$^(_PJa-0VGo#g3ZVuhI zXH^m;gdFS0rj0TziC}0|OSnNCArTsH+E?hY{$aSOuNtIFKMgmuXR?3>sRrTZ@W$cV zd4J6a?|7p(>hz%*H}=}pXXV|<=$MRF&T@90WM#_A=hkI?t>!{=Jsz-ZtF*2N6W0Or z=1C8W->F^~bzKRq@k{i)i17GuQ}Q@S?A}Tsaw+3L4_m;qv=F1DYlxYW=21Ns7?Q`! z1AjYX#4Psk7C;uWA)?j3xAKmxl5wDuF1QP{$B~q9c+eH*r>W;T8AmTaB(u3S8|%1I zr6-vjtK#gQe_zC^8<)o-%Rau^Q}wE53ZTO7Wrep&^70l*!bXWz1{|D6Z zUhVkW?Wl;?1m0?wM53Ot=8Bnx8JXMu7-VAzW>_KFoA%%jI(Dv|kG-Q(FvcF^0#vBg z5>L2#blsRi+icK3@8xM`QPCbuPi{+o$XY*TI8Q2nw_N70UFMd?z2psk$VN_gwOQ3$ zWJ8w65}>Kol5zWXUZ;pXGaq@l>xv*B>N*+aC38+0pQ=nqgs3Sf%=I;fjfI+4VWO>@ zQ&IO#fTFMUtFEP#X5kN~+xmR1xO9Cu^npb_82aigF7?xO;niafIm#;)g;4tUA5`R- zUW?^#EbsDM<_w%#UPAF!aWnKFY#lE-om%2icoAMZ`nZty>{VZv085g##x zSl{in3lr%mG<)8EOe&9GHFQ!OTDCo1TIH}B;>k5=X$#Id)8FE+d9yR4FhFPZHNVZ} zX!CrmC{Y&k5S2MPI4Yq^cD6R5+N&$J5Lz;IVi#yIV$Yx=E3bKJ~m z`m`60y+V+X;UZ#%`jq|p<$P=*e}gmB^$g%XMe^;8Ui3yN%=H|&&V#1*R&-q?g`kU%&1(q4vP+LQ5$;`5o! zM{}(hqPOuJ8;#|y^z<*`LG1*5b7dILx*m$hP3q(I$OXbA;fQCI>BGBPnn$+RpMEdL zzQ(Zl>4M{j3_FRXfp$7#(~r#E?DJw+jnZMFv$V6s$0lfbQR3DZTI|o1<=RH7ex@x5*zUQ z*Jo(Tv@HtnbrNV|i+0@A`Jvj5;Ss#|4b-BAnYXtoca{UaIaJxB%9@TBIT$FT4W`MX zx}$hA#07FzEpM{H(04br@pIHlZi!RK2xMkWc@f7O5|R<}f;?t_m}nMA4B7TBb%823 zy}SEVt*3!U8cR+=8a|Bz&m61T-=q*P_bzslN^-rq6xN&C@FKDTgj?9BbkRDE?M9Lp z9F5MR_3G#J{i{-&W{J=I7kGl09|kv{e4eVU*`#Ny#9=i?<%p14M2Tua>Hzq--Onuc z28Y`4v~mL7#dAp+nlEU{+&dDNoU8+UQ(PT|L_w>e^>|k-d-Ye3IGh35j+GockDT$Z z5379+*hmKO=n>J8I=We`{rTpCc5iMwhbvX>Nd^iM&+AH%GPH6`p-tk&P*Zh?u6-13 zNP0xdJDt-0;O3E6^Aq?Pxnk-9Ufykf9Y5ZbiFbK%R=sRrIzoMD)o3Gi&PxO^OS6=_;tgoQ#M1JwV?*H$s753;u7CY-=GsOpIKC3w*{$JWkIj6 z$|%5LG|Jfdc+66K)9XR|!MV@y*&8r{A6^{;{xXt&@$fFJY1@b{pab+0WEY;xB@xtB zdPgProm6(JmFRK?@-qe;YVA}@2ai6?RnSqjyfo$|XCwBg-SG3Ji8RVP z3wuFT88VNCiCDRGW2{2<``T6(e@dU7rpqS@VmQ;F0X$x$cAI|nRoI?NdMsblT^gPq zlYE~~JFwBwE3BTp7Zy+H?A~I_+oW`>BdJbXKx4cRAFs9$L6>21@pdl79by9BbG|8T zL7RA+YNdN^ad3%43J$;V#Iq^eHVixbI^yA@O=%VuT*GG%5h}1-l59h6N^5O%^(=01 zhse5=aRj99>Mhz9ejQA~E1;D`B9LLuhQBWFWZFuVC9NzKq%t>H^bVoISGMBQUWz-B zvofe-!aU)(8ritBo2iBkVwED;)L{bKIoP?-Bs(z3T(h@Rrxj&)vY#8g8pxn=EGp~T zu4bVs5zc?yo%Oa^;%zd#ZjzyFt0s2qEg;u+4A%LRJmYr_xBu@Beb;%T1ztz9zyk|S z!9PB*_z#M=4gUA4O^`C7R>V(7uAaJGCx3+3PWQG)8#;{pGP)e$#sWRT5V7=)Q!Vk) zTBQ8CRO5A$A#^K==yxcanw%h5EYvG~1Fjnzmf>Z&r)?=Us4ZY0dv8AAf~Y5mjwbfu zJK35LT+t0_-do;;GGY6l?Wr_fZod+PV(3c&ob?hO6dL6a?QTOnsZ`>a$Q0WxOF3X7 zH9sbz^>ZRd0zVpU)6hF9qtw}DTE~wmmr~n4$ZCBycP$-_bSTn>SFfMQNA|F`4O{Rk zc)}23rDe6sFUX+Tni?BCCq+nQ*`ZXh&=8s2S5g;5psvh3!yxHpsYgl&QjIE@nbF?# zJ4Kn6T9_Hakpu2Y$_K(hA7mJW>u{gm+p!FA| zB{S;#bYuq>40W9unFW{FhgtYfe_xw=E@0tgZ~M?K##T|>vfY9_0Xi^8j=C90B^64Y znMye=n39VFRpm!1)*#%pnJFqhxGa>vE|d@hkWPhg7LlIq9JVCT%fv;uqvn2iQfCxG z*Ma1(2*^HbY1KvMne&HSNEBO-mdCX)MvBZRiShz7Rx*c^K^KT;x3=*{c6J;E6LARP z-wpZT$Ey6ZB@PqiVR0v=JY`@I z>zY_2c_?L+Gy{HYaPf{jcvJotBX?3Dsgg?5fK;b{*j45?yUr5FMBM4+81F}Rpn?FC zEBeFa3R?)jH&K5A#R&v1s7YlSt8E}WBrl{V`p3GWse1@_4)SFaz(Z+Ju|`YG5iA(A zzAq-Mnt@c}=KEA5fvK+3KWe^z#Fh9!2~01B8t+0t|7JMq6%vR&3Rdbt0qCy~9)%iC zLv5G>G#|TLD@nMt7w2(WVX5t7?4CFVTAn%ykjVuChOG=2_VH%Vb7Cd7YuuLpmwQZu z(L_mU6nf*IZxN@)A9|hNXqA^CZAD>({fPuy*w33im$JB^O0sMhS@8|AUQ2ByAMOM>CrEpWjYdj9)>9QlxyD|9V%+G!@4YzoI0V8MibzhK-nx&iF% zsbZQcTLMTLUy_*$=PIM9J{UUK5qpfGzE)XGBJ(S3{8#P_VeK0TN0@SB7RO=yI8=tJ zG;HS416B$=6~b$lfkV52jWY0pGhT&sENtUTz_=Q{?V6;(t>%n z>2;T0d_qx@Zg>Fdk{KL13I%wr%?7!G9ZPG<9vKJjS4tDElC@<56W9J>X{sNV=BDN- zMP3oSKn3-_ZTmv;JSzho4fIN8H3Gv8pr52U^E_>gbQjsf3%Ov97`o10>ZXg+oH9ZP z7c{>08Z3?oQV&!sAR>c0Wa>gjPYN$QqBHHDvy~FbKOl#K9A?o3EiC4+h&0bqE~aKgvw`>Xc==KWtWPuUpN|(+ zNnjt~I;jr=>Iq7zQlm`p(Sz}KQ9NzSl(#rSPB;;grM2MP{`qHqrAnXQgd z6Vs_jqr*$|uS)puAACkCBi~taPYc@HMZJ19Cdg6LzCfs3$~Rx?1|+}&NP*EWRQWnk z`7vX^YHwa_C#m&oS@PU#HDO7;m06hopng^q6SuaV6XAj)=SpbrSsl*sOD-XHCL${( zZHry|_q);EvN4p+_iRsZ=a6819P=61aP~P|@K_0U+Ux4j?zSMUDHCy&+)_qEEb`c7rqOPDhXSc68@aMcjgf5v{ZQPUWiYhMbW|pt~f4%z8LBi=?# zUFS6-=ux1*UPt`7>>Eflx|vOyv}R9$GUuz+WFU$Uzpci7XMfG7sB$ixdt!Dc#b_OE z9BdE2#P;%V^9jj;Jo5Idn@1B&QUkblF`O+i99NaltK!lreOws1)8-%PDZE1{KiQbT zt^RIWhu@ht>!)d{|6frDWs)`hQxSRheZ&GwW=#G;#5?(tZyYckasb5*%q=?Uaoj zPx3-vz=oM01$F5QBT4J3(RtN*lQ6i2K>(79i`e?N$Em2&L{rq|wiqp6I=-4<-JKp2 z+!2yq@fKQXYHq>z!rA52uJ==WGNUcEyRAMu~iZDY>f7*MCQ4Zbo&igk1nUiR;$nATVBJ80A7J@!CUYr zrmyvo+8!IFC>XsHbW=OYTiiBM7;@(^u)29e<7XHp;4EV&70dkfModw#3B7Z4CLvxcfSKk46=mqV?ni?@e77Jf#dV;=o4Z(^<4LiXI*v z3lemII!Cwg*VUD9r#RH01-ytH04~9zG~~jr%O~m0mTsQd5b{@BIM@fWVjaZmWjoTaZSm zyKU~|8CY{KGcx!m@?8i+(&za$XTJkqdO`nw*I-RT)(~tD&EoEdPG|f~xjtG}VdYkk z=UeCSMlzhze*3^0j;yB()jgctw)8nsby>-Jhjv+-EpMNW8W=u^49>38K9x9bRlq_$Vz zOi&3MSx-p`-VYmjgCF1!!%8_84Zh7I0=d^o!D(tJW=1dn57gKHhI;Eiqh99$eH!=8 zwD#k2Pn64a_ zNI}3ga(g?Rsr{XU4W@-B<{lSxCK?wmYv$-Rq^%s0*VM5l+0}qI?o}Om;KMLu{|)3{ zcPGBdH7xbt*|vAn%1if4&?uUm!~(%q*5RB=?1*9f@63ubDZYG$@sU%Cp^Hx;BSM# z*kD%F#?j;lW8E*&$#Z5_YSiZ?{TKHI@ylj7_TH5xX2ECWL{4&Tilr^=>fcMgmv&G? zxMi;L4^tA4ulroXuKvy4_U z{?QuQ1yX%|O=2+r4BkIOejoY|oTeGO|gG#&?i58x1hUbFCUn07d-z;1)zs7~~e~b%xmD@}IJ@89G9te9G z&HHC5Xp=UCXHO>O2DfdpQ!iC`#xr4+vtKE`QdZ@I-taTCPH@4mtoM$yKbS3GxHan$ z`RY~<`!Z*F{Tj^a5On-Bu0C$-0;vf)9oN!OBSs&*gcAIqSx^CGP4a&j2L@fy?p=A(_?%3(rQ9nUvag1$=iH27oX{~bJ00FR zyvzKl;h}tR2)7)-B>BntQe&8N7Gqr7D@}I(eP-B&mjNS_aOf% z5agpraY&d_;=RuN5yAZc{nc-vpZhcD8=rhawAmghVQ`uk@q=x4d;FOU6z5N5pd{+8 zWVHLEr3>JD8y6QHaF@|N=6@39>;8=L?f%*Uj7f_)VhnrhUVvcbe_2UR`?z)$eC9S6=uVTne(`T?`@1OL`cc>)qx=w}8Ni8W z{B+_H%sAQ=ehZpQKb-gmSRh=L#h&EbZF`Kz zHt6dLXC;SEdNlRyU<^Bq`uBx|aGCsk}gl$hA zXV;p75`z z|2w=-@XIGn zlQLdbTaJ*hEQ)a6g1(Vb}gXBNy-O(@2Y^D zWF$D!vfip3o~5gox+sdLOtO%g5a2fSSqNnr5V;!uZ*~Fz#1g1LGX_xFu0;#V%*sqd z%KUri|JX|WFBPE0KGP$DvQXcs`xAmcHO#})Mt{g3t8LWxOkA$MgLrlqH5ON6mxNP}t zRTkIP)pO4Jkz(3;b5=f*9c^KWohzVx8~snaCrm*7;JIthIV|oe(Oaen1i|~8$51Bn z=7`WQBw-KWKC{nvJ_js=S5^pMb4soZ}#$2CYgdVAJuMjR8QW*_|E$#sP zNl8GL;oeAoQ?R3Au$a{=w>7oNlyloS*>Jkux!$k=1V{r%cUMC-eX|Q5$4JXEt?I@%5heg95w>eD6r6p z_6EaZ`}8|TCGs=Y)uP03LwDh1(%ig@^g_;176h*j`Nf-x)ayIy%pyySBAfL$J(}}) zbVi#>agXI$rQzM$Im`Xsw~HZcMKaETyhGCPfw2!lWXYItePjy@exOpsJf!~Q&gl`~|%)$cOS88VO4e2FRld|O6w!7$< zA=hzHRmI`pFg_|<5-Htq(F~fSY<1hYo~Ph1koSd|8r1JWVbiP}ky@Pgu2(11NiRpo@Xqk>3u>-c0zyvwQqUb{_J@I) z6on&9SNXO3Dnt!KiaX3kwWZj|*!1sSNIkUYrAS!YrU;ksXN()&A;ybT{2B#%ibp6U zwR)lcRecd3or%ATUMVKY-nwi0dd4Q6sV}yut&l**Ap@6AUY?&%Q`_ybW|mnyg}Y;| zl?2yd_`a_)6SUh34K4W_$O+Zu!gb7MQJQlzrnIswg)WCso$WJah;78(-NCX_A;U$< zy_;mJTzzhhMFkBr#23_M?uuIWuMV;#XLI3S9KYx@Rc#knFH{|7_$SkoU8{{GmL?pQ zO*@Vs?3PTO`?1iS7OBEti=c)+j#Hx}!Z#QONf-8s?<2L21N1snTvOU~&1VMjh3^x87QsW@%Xx1r^pY`|9u_YUqNC(i&a76pUY~YIMh|@#ouL)A zY8xB`c3kcn*&f(jbK#eysE-TMyash%`0}V_Sqqq#hI%>^ies zI9hV(#0G7P3KN<)lG#^Mw9%eEhL7r5UxS%7OzqhzUv}|QB8K-SGnKZ02p7yx?WZ!$ z))S7r&~+2Z(e(6?FbyNe5r-bBlS6IcIRPxk&e&)@Zz-&iB(Rr=;py?kOA_&^~5eTiO@5I&<9U?N;|$xeSOGj7-iU zA@P7rx2ea{E9Vg$awe8cPp0oKLb_rJN8Y+5$W2&p53QJXnz&%QL^VF9$x}jE4JyyH zP}&%agy*qk>>$f!c|Vdqxp7!`%^lvgVqO{C{a9TV#f{9A7I9}4J*Oxe60>KW=Y*Gz z85bhl$05Ml&NuFMbE{4`72A+u`FL|sQQ5~;cRlM^od_aJ({h$1p{sviT3r6_ zx>I)OMv_{7X~AU@K;GDwt=nzgnEd#mOQIAC#r~&Pa9thrMzju;Q+6FaXtJvrC5N zk8rt7h*zIenM{qkO9!TVXK29;H{?gvU2}+M27;XqWV$Pt!@`LlM9Q#k+-bz6Ev>Rj zH+ZeTGY=L4YwfywJa8$l0NK3SkY|hFj=buzv~+E43!lH{mlaoLEN)AG|9#`f@D@UK zgo(0HrMvPid*D6#(3*Fc%usbKzx*dbuRnbUgZicwY|F)FqVSm?4$gUSr*6(e4+b~w zy{QX7qW4e+ZPo7n4*ALFjFi4<&VX*E% z$);O86zn3X;UZ{bNS4{SgvA~&SCz}!47-FuJ~R{iW!w?B%hT1D7?j)WcCe0{&09K( zfyJKWOye|L*}Vc!EV$T?S;n<5KX=Xwy{f#Ie{oOjDu3uo$jgE`kSx{2IZi!DUKS)x zdBKB1aZu={AFLFv4dYLwC?3b{f5D0|lihZyC~cmDf`>WA zd2W(s6QwrIc7fbZh9ZIu3&Pc9$rTqzxwx9+^ghySD9UEG$00lkkLpTWPkd+~x|L zXXzNFIsMi+!iTkj!8ZxhE7_Ga({>$>6a}AdHXNkqixRzK*i4g0@(m;t(ic!nz|ZQi z&g>%7NewS8^1!`OD`>G@j!Ni>N2QLAk z+gwlB`g)dN&!FVvvm6EfP;RBynK^Q?Y3yj4q%0`psxpjE8Rgznr>obQTk2UgK_(^* zC@Hv{_yIM%_RgjGW2JFV38cE0CFI2r>qkFlDui{8SpeIzs4+fnRI4CLSE`ueNq1!c zqCgC=pv#mm4miR>q>FISw7#^fzpcBSVLj$lzvQeTb37wl%QqWIt6e(1dyWC*eYIbv z|4s(~Z*yQ;AY}$#LOkC!tiiu&Si|>_JUM=l8@B(B+%Q&vG@dubTF7pPS=s9E@6t*T^>qck+M1gD-GJ2@>!uU>P+|G2U>e)`5Y(0F6Ns++HMHMN%_A5O}c%00Q4qQ_8#An!rZr4rI}hUL40Gz*Dn`sK50 zV{xP7eTh^xfn*{x^eCHpYAlpZV|ES(p7zlr8_`u0r&}%rU;Xa}IFOp_CsQ7~X6}@E zToS7B=o{Ah9Re&tR3j&=gpGY#p*uUpJ-trL{TH*a_)yyf8SqmK)$je~oG9yRK=ti0@@NdF8gB>b}UO z7o^yrsA{GuITIya>mX}r6c4=9m7GcV{?2BdIN_%By3&EPFM4T9?)`LiIt^dLaRI-K zRt*_u3R#T#fLhRmS982iqf+x=C@TFxSoaNy%=Eyqwww^P-$ADhMWV%d0FvWswixhR zv~31-W7+E}6X&I6)%mP#b9oDNRjO*?Um#XaYG%s$&XFtyII<q(jHUnstcDb{?vBx8i zo(3m6d0JnM)!vxylop8P72MZ?Q)MbxH|W?^lX<%;AIrI2g5|$_J`k)g$t5l~bHulK zj_&EuH=z5VM28cXi{J{QV&llhvuftr+J3NO@3Tsb(G~ZUgLTi$wL|U2)N28W&Br9{ z5n@M=tF|GngO(@c26#78rbvmNTW=-_NY6KQ9Owu(wG0mmgj`{ps_&LDl*hoEUxx@| z42p3r#-M0jM6H`S=i9?;_9FLsGVU6fziX|Tbs<e=Y zJ_QI>iEU*JjaJGqBovChP4+6Na2o?l3|hT}%lFQhujDsZ@GS_J%P$R@ePe$NWa(8wiH&wA028S`Tw|9TSn3)g6C{LC>C)m(M3q&+ST}gY@%Fb?vd& zm;u96*;+Z_edf$}*0^Fw#Ob$<`bRKr6BsOyjFhl*gc81RwJ3yY$Rv%1YF_nD)M#4R zKsfo;lFEvB8(&Ndn1Rq21=SgHW@WQyTHWp-E$}t2y=IkMppRv88Gjy)A%XLjBJ+l! zY;q7@HaULy$^{eeExqzv`_xv)Nmp&PNjFlp;spU+t?B;tPTc0$>%E8NRd!3UrG44X9KcRv# z`K21IJR4bre>D%1ffZ?BA(3R!)Rkz>UrRh&Sbfa@>Kqw6#^Om5V_w-}{y_EjG3;um z{kQ6B>zEnY?e=RGm{OZzQb_V5{0rt#4)h$XAX|q;C(=@j{PXNkJCa0<;2-ak;g**h z35LMK*0D@iGgX5kiwMnTh(|V6^V1|g3L453=hNd)S~Io6Uz|vL`=|^E#8G%x`Dnr* zs6hvG#u^&x7fs={h1=EoZq!>vOU}d2UU%i|M~%qcbSE9_PUI%^JIQ>3Rw+1Mkm=A! zwj!)eI3UMCuv6%R=YQeQG}QY*j2O33y z*5oG@VV!7WRqm>t6_nmgKyPp6A1d-%t;(vlY+lVPEN5o5 zK%fS9+qAEgI?d;AJ4bX4Q&}RzFx0cC3QJP3#4CY+KvwSwjebSiHpd~oH-j;tLc%G3 z*#$U?*?pY(5S)*>yuvEG`!c8Sb-;53W)(nKEk(>^wK-uyQ^lwitB(A0STd8rtyRX# z9wkCl*h^+BYKYN|H$+Q?N7IsEjhTy4ZIk!dek#V8$b33)fR;e0(#IlpX{%SzVh|TebNRTp7g-+B%KdZP~)Yol~d7$}gBD-38oG2h=G-G`x`OK>{OEEHUkz z*0{Knp{H3E-Ij+Tco;*o1cYtQg^%L^X5?|1Ugk`0X{~Jrro!ki0Y$qeqS=<*JUbgR~gZ*0JNFUjnGf*n^lv7Qi?M#+s=G8G`ovP>b|@0OglnEtU_ZrFj^ z7$7;XGA1;@=7j4 zjcK>DGPBZd|D=$zlh{CHk04H`01b`KI3mPxKRiXX9O2_J4#lP8F%B@#BjclJh(ui- zzAu$~K$@Eso<^9}u?w+eme!Ahhh%+BQf%_0f0E))LZoJ#*`}yV{?45#|Eycfzxe{2 zGX9IdvARPDcPry7jWW3tlZU!+z?YqS;A%@=>5y{=!z9 z^@u#jvduS#>kF0)Xeh)ip(2_0lkHJHkRY4i>M}}FK2*eVbwo+K%0>M4AJ<63%sF{| z(@g&0r94dG{)2jxL$>m5USiu69wWzDccUbAhs9d;lkmN-t~DtXOimfo?VQ*(isDS4 z{X~^tjq^x)J2UxiChU>lI%7X=PgvW#NpwKU>TWrZuqOBf=$LwY%gZ}2)HR-&076j# zshH^_yEhD$GAa0Bz0X*6rh>hD+60T^8W<|9dbN4sU{S0DPa=0t*#5h?JfM5k5=Cb3 zCMcut1CD#x_P~9Y)f}T?5T^6wzEcw*HwCDL;P8askXbtMz&%2m&?;Dr?)Tg`1Q~dz zg_NAE*`?emJe^|+;JRCbwjhG!3|0!3-G3GhT@HSJFHqwDYVSLvqH4A_8xb&&M35lR zNDvSaBT*d0ZH9NiA`*vY3885@AZCb z=9@dS?yU9Aj6eFvsdf5P)vjH8*REalJd_e-GtVW>*>WzIenl0xfw49QCWPMwadKZ+ zf>a}s7svU_1$wJHL_98}2A#$=p?t-l0RRe~2@@7-IIs?cotHivKm`!>5bjj=X?!%nk-sH1ZK%)Zr4KA5-TAsHo_XA+Bo5j ztSvR70yBfWA@FrpLhdV$DeqjLcNidG2L{cFMF8*#n*z4CfZtoO`M`4GRX*(FHBiay zF~~hnjQPVzFPy;3);iNmE9O3iW1#h@oO+|a?FG@pY?nyEi%nfO1P4>y)5$}?{%~@f z`ay*MG#}-pk==o_25aHxEA~aC)~6Mkbr~ZCV@BB6uGZzuC_XQgVR7#PlRg%u(wAq~ zs&;R>L28%$sA_q*A+@^nC~viB?M2PeJAIcbE7}jduLJr$$ja{Y2uSPV>^d5)s*Rzc zu7fJiHsze#wm)(yr=M%!*idXCBh(n$uEOa$x}ek~ME4&s>VK5-Hq$QKN)B5`ZWa?2 zxP2Zl{wA67c|e?VDEe_0h}T>EofkJ&d-_q1Etpd3HXQnH${-UU9if0yK2zj0zXb9V z6Bp|twwsqYBH8c>#rRBmnvmjdl?(us+vnuzb?Xw|##pE%({S2}$_n&v<8N#eV61{3 zO}k>4=F);)Bwv@g*Yy*1zmv<1Ebx7i;Y?aB^X=S$33@VRqr=E*z{u%-L*>w*(n|w* z(rcI+Le5u42$q=UZJJGFm|6cLjByEVkbw^XHF(}kocY04B}oqm0(U}BSa}QZK)K{-%8V^4 ziS``Eo;JHqyD%;ky2^-RAI;SA5w&ATni5mm*Wz~(r5a?jX5uKPzF8uY3iq`$oXKv- zAW(V@lo0zC-OT+MFA|~DUXpypvQpBi6ix_%RJg98X~u9$8VrgX7d16r>c4OUs6heo|J(&|)iWmN8)$4Aq&6n2imVB8&)hqdZ^09&BBa@XGrQ$936rXxnqzG1W?)&9i+ZqG)@b8JD6YsOL13 z)#mO@_RTJP4@zB~F{Ma>{5*O(q`nE2J5|>mtj7$TzSW66X1V+6hdWKDH8?`TGo5vXpXD!ys`>?l_G zO;ueVkhQ4}$kh9Emfh?uFLGjR>UIafgB^;K-QbA#F9Ytjic|_1LS70ju3PAW&WwK1 z%SY$#*N(jThU3OMO{YNAO+xgKCbEn3ZgF0U(`A&}^H!cvp02zBmyFJ$Rf`u@8J=|Uq(#FwRm z#@2N|ecri|Kt3rGci{rr&MgnZ6+dUjH!U%Hfiif4>Esc}G)d5mMn}?RIF;e8{-r_b z`nGP&bsCuI}LEun{>*xtOid^TmaI^}GHoRQt^x zw<0yXoMahATQvgXQu#l8ky1tR$}Sm$`(?N0?@Y1PI^Qw&So*pHB1{#*8ErHIK4{}b zHyYS9OibzCO*7~BTRZ=?&FPMwFP$kD+x7Yy#sxo~dhA&gnF92bduX#U_0Zb^oF@CS zhuPq1R?Tw4w?U+5+bo>XuAYqdu|OO~cHVBg>QeT@J+Y2nR>8jM zwATO(33$dN(1#Q;g9BfXx$?czHBNVg*WR8cny31jo@FSHImhKNa?nB~&u1^&9IUGP z@FhN?@6Wy`K8a!rz8Z#P74KR6N?FbGypwLe;RRON98;;W5uUQ{f2Cx&j7&xO{6oW< z3l|=eLhHnD)mAK=`+U9otFTK`AGi#-K-j!{(2}y~!$0YkrF}lkEU;I7Wh2 zU@@{xmr6ErW;C7$reu1_V#tmfO1&LoN-ADR*U5ckObOE?KK=BWV=`&TexqjK@YKDU z%95m2k$#nB>Z5HLn}M%koL*%a;!4rw%#1E2$mGRSz`3j9-JOAQCE%}y5W-}ybAyjAA^WS0r-W1GQFll05;y1x0BuC>xBQj z`M1~DV~}eDC$@j)5JB#Bccjo-EzjlYp5ul7jOwd$h&LQQ=_4O-j+l{PuZx%)C=IfS zqu1XV)aovJ<&SC^`UxiUuPCw!5!mg%{qk<7`*L$lb{gVCj|&A?L;%g3`)psIM;R8y zZY)m+R^~O&g!8rQhxcSXYvEeE^!2ZIlwjcg4Wu@X%M9u%C|<=wHx;UM)t4ku zLKrI2$M*^7+2}c$=ZmqeY9l+)F`j!LHnP6CN|e-0L50FgXi&b&AIBzcMG~B*dg?B` zTGJjM>;eX1qtWQ1s2u(Blv3}&UC8xsb%%OaC&Iq1b%4fGUziIK%%qim$?@mF04_A& z#IU^x>5Lja9VSGcsyR26D?YJ*t`l4RXahxNZBW(cA~_yzU(StN+y2j=plmSwi7(6h(*+ zt^1<}^2d&fxi)RW=3bO*4$*86M;sk1 z<+}e`;1;P;#ypzw{q%M^*#u;U!uK8S>>KQ12QzD$M%j>NU1vs^6oUH+tGq8fK(B;l zx0n&rkTSz0`cSX#VxDTgZS<|^>oHe9UKp2*3j8u_UPPk5Qykm_pHagJ$@*wB&9HqF zfQNgn$`X0&ZcwcolwdtD=hj{#)4J|9HU(q zK#}TBdf*Sopl~S-HGT8FiFo_obfxZCJ7T5JgweO7_7h~<*IR;-T9#u?Q zaGf27qGhjvtHEx6b-j=+JVDd7I5me(HY2Y%(@Pa&-8LqQ^uS&8yzbV(SX)(A=BaEJ z9;@}8%b#-GjZrXKj-6S7XQ8T&0HqPscmcvb8_{vmz7pZ*&a9v%CIKOAn<15j3#4IjEdY?I!rRJoX zdS23?P^xj%FF%S~GgaUziBmUU*ldNp+FK>p&u`p5R;4uMHX-Fg4(qhue#pBOaA(Mp zWVdK`sCdw4Y^;Cs5T6tt!7R?6H&Y;=`zfhkS@dd-=OmADsDZxZ#IjRsO+yJh7oItB z5qhpH+Tw-?qII}BPdM~Vepmi@#Ydypc}-#@zWm-qcF1E8Rpa@(oZxbiF=?;z*ZibX!7 z1wOmXxq#`mvfrkWeO;|WaJ;#g{b!izDZA7S?6B9z2t=T128@co=}?qrb)-OSxOGdx zt4HvG&yKw3;l$-BwF+06(2K^IJTr_l+9@rWPmaEqIPAtNta`RTl;m`nFC5g!ch_Fs zo01ydoMC5+4k9eh)>txBa~qqRm}m&y&oc}>w|7HEf$N59Z1uHTvRws)vp^FmzCeJ9T~QJwFo z^=!a!45wdjZ-3d|WeB6MQto+5fAX8b9lV)5+uQeemK+dXSiiv(+q#@K>ZS{zL>+?| z>}aPjt9DE=pS&+Rk0YkVi=DA1(*@u_iJrXdDzFWP=BQO4qH&D5B#>R=Iz|J?nSnP=)ZIm6UeQmO8T8$U=G+|IG?j&5>g~Z8CQ(9nbRnxZ@FaPRLLQ_A-HvD?XY&! zD8B6lCKFB|OpOi5RrD@AsvwRszd%u1AYdO{7fI_X1I5y6-IQfk&Gfp4^{}5Nqd-my zV~B>+h;w@{-zJqCO;-AJijvNkM%hf~7{uZZ4+T>^@ggkHBfGR;&cvx(u3Gyr88mmf zq=Nu`nvtABWd-L0N44Q3>6bay!Mpth1}NV_r>`H*dDg;SWnjvCx3JGNS1OgH8v&c%2ERzGD8Wd;12+w?LoM0;P%{emAB9HG zxN|;bBOD6bD=u>=Y~X4|kDu=2yY5^Z^ghY;_POzZ=<7GkKz@grX7^Q0P5sF2I)#ca z*vf#9vT=73+q&$b`D~+S^#XJ}s5lK{2026)hJYX@+lv{iiOStc!`_k7@Nm2V;80#u zrlPp{im52P+=>30;w=xx?cPK$A*`(}`aG|8TLaq=dF;#)jbfU>)qSS%x2#*e>d0IW zBW7E)W&K6XLTh!Mfp^ij@%Kr0s{%*OA_diGCv9DK?$c@ZI_+;l2&Ck4yQeiSxbw6m zzS1`dVNZcx*T45dMI_x>lf_h>?Z>{&G@%~`+Oqy*BZ%)G8$p-qw$uxM9K*xh#ZT{D_E?A?F@eP z&UUYhjWMHZjie6fP>^H*CC!Y4x%8fM|IWnOlIU-+R1=p2rOv0(Q#+INc=kQJ*E^Vb zp)0)1EexmTh%U5ljxiCm8Sb@XcOBP$TwNI+JP|(S5j?wBBsjg^3K&~=WLsQKMKB}O z{_Ui`>N8{60G*Guz8+FnmW=%mm2eOYM{%eN^yNi)y8d2GDn$ zJ?3)z6h#9VCy>-K;y3D0hO$-&XIH*sOWZg9oW%>d7U{8U2wo+~UUGZtu8q2s@Hh^N zO5&547i&7Za4jPajYTB*d9L;vCT^!)@)E$1w3!6K$;K7pCMPFb)!W-fs66cGVy}$B zsC<@|jCC;!ZTdt=9Gfxm#@yJ{81_N$%KFn6)E-LmEEk%a!l)GK*!`blVWDsbz4MPsAC2=P(ol;|#9Qe7;DNE=vp*EP8FUHdQIjx|S?lX5m6^Zt$%53B(WxEgafeG{ z3B~^ELCs`$Vax}I?)7YBY2;w5>P6bn=zF@bze`7iaT?C?iRwvC3}^a3C+WD}lO^1w zLF6n;prshRvp(Kac}q$zh|F@Z(`9gShqYzB(PV*hK{>mc7mijAhoJ!|C5C1F*Ou2a zIJ_sS2yq7^Bj#To&PVDj8zk~SbF{a4o8&FQQykYTzn73$tSyfmR@#h!Jx{+M~<5gXPjLRt8ri=4Geq1mYI!MYfl8Td@Z&TeP9c19{w; zF%8|zc2|p`E=Gmp)n2>_{cZETOj>X6TWek8-h#N)KLHZtW%tMWE4?EwP0mZI-Cm_? zDa{x33hzsm?d{t#lBxE*@n)v$e6?SYhe-e(=#(tTnj`yYT8CY^DFtt>eHP^dMJtLUVbdRT&8!t^funH&sUJtX5@mG9EpXf4w%k3> zhvfw!ALVjNcoom6_ZaqVs_&|09vF=~#cd2%y390E+K%lcQ75*z&!2W18)Z4X(9F~h zTUZ5W$YptH)>c)>Mm>(a?S8W_o`}Bj(k+eZV(yK&=PszUi?*rErA7lLJyA#0$+cih z!y!aDBbTEfC`c`WhhI^)pR@|RR?B}7g3Z{9Y0cxdZK3V>Xfw!2V8EPCxieSm{G#K7QzEK)DUQb*g1PWec7>_#h>1D-racQjMlHqV-0^n|qgK8IMM8hv2NRz=3QI zLg;6ec|RMT|wi)#HlygHrEi(XTlmm4e%9eQLi z%(10>0d>0kr)(Hx>qS+9z<0}UXB~q&tBc^{mH05>&wWG%U6Ls>bE^*VU5Qc_7K|1a z&(1QP$+i3i+rHM8wqQgX2A?&rJqA_44|weo{>q^N*{N5(k_uOB={h91Ic;zfIznal zeqNu_X+cfP&z?2-gr7%OZZ2tR{=C{j5utp=6eF&{)ZCm+r59%&?ZG)5@_U{zGeViw zk^Qb_(5;-)KCBs8=_CM^M~R1CE%VAVxIWcGbJCLPd+5`IVF;KdH7NGFcK6w<^z@>n zOqpSbmbY2yr!p^oX5Bm;K9}k`yV^ULr6>;qMLv@litZJf71|Vf;bx|>m78aCO0S61 zU*=9Y^IEN5*pZ{A%R-UOTVIYtwWiQcs>=AIZz+xODj~npn1pa+jCvy~u+$rdDb{Zx zst8EtvJ95sW;yk>S3721kNQ;^DE3O?gK#rzEBVE0F|gcS;FMRKL1=K(#ZLnSgN~fw zyF)@{KQ6dXL_E9#7og>u%zp}3v6ED4{}yNUU4wnl)`o!VKTVyUWK$JSYI?Ua{Jj3OM;$g_FWKBkEewETnj3HIM%mFN8Qlwas|selm-RH=u|J8V zM7)?kEAz_V^?(_pZ#AQyg-QDR-(@ekj%44|D5U;5ey78Q3K`mZ+G*cLKb7$-$BBAo zERD^jtTmdsNXu}39&Y|bPkY*wf7YqnOj8yt*M(7RVrJ+o_lC?A2RU5lE3Z;3s}yze zNY&wu?4s*}VLBI1FMxD=eIuW7bK9I!woZJ;V9RhzvV?PgNart=`Im0o5l;nqDHpnH z=&0GX?p^KK6}EhMtALX6(R;=Vo#-s{uIn+Mwm#1*vvVW_f3%W{R$jAx^-ClFrO*GL zzV`Q@HiGwmNOBx%>`#O3-%$N^4+nb04&1}Be~vu4a%1rKl^f`~!M$x@$AqiHqab)N zBnPZgQxECzy`C-J4iTo@%Q1yfd;$>3Pso8{)#Z95zs&EV6SFwgr3Gc_V@QXkn>sXx zE3%m!d-Xe$T_6iW1igk4WIh634s_VAzpM;P@z~{9sommsU_)aX-ZF=YgDh-z}1GTm&q zyCnv`+eiEYp!?OY%AnDbNv(ejunkFoQvR7Vd+=z(0h=EU1k!9vx|dVS_0>~f5hPuU zi3v;ml;V!wfAF~=ZMf9hxEf$UNUgfNK!aU9R9tj}(RLU;Q_bXIE}k>K)n+R3GWrtX z(SSch)8vCwHf$sqr-!{g0}n~m`np^q9b_T$1CL;?8;+&!JC5m@M(aLd{Z9el_&451-C%ZQS2w!LpA_L0qhY!{-ZG;cuQc+ z@ff78aSTE(63!*^d^f>b6w~fuc+>C%kYf-y6IhC>VuFrHnMXC?=73@)9!+sT&-eh)`fnS66~Rd>;8bJ3dc=tn zFbM-N^4Wf?(fL=6$DlXS06sn_?HGjUgIhGx!TUY}nCQMhA3stPY<{8H;XC=aBsT7^ zw+7%Ry#a6jYQu@ZD&@UZ0eauxnu0wd+5L}_(ELjhjln;~y3ZPO(jCB>ME%tUVBF?v ze#rIrmLL})A56j^i@yuw@h@R8;Z%RM7r6wP0VIpq|4keQe~aVqLGkfF{B1Op(kbF{ zi}T5eyZh(AQpcK;vdQh&iE&V zI~nZqzXqEe&+)f%{!8CV#{**cP2o-kJO9^UyJ2{LO$V+2_w_4E`NwP_g?Iiw*Cu|= zwSVY2dK*0Y+jRfGOZwQOf2-Yz=Kgoh?myE1eG>j_R{xLm|8lbbl4tv)`@b!4!2h4v zeZ+}B{huwL{!FC*rHS-kc8U1E<3y;_a6Ug)=-J>8x}k^IxZYn2@4=vzC^V@xp6b`? zKb<0WvABR12uz+IgAx*dy%v2<^N_{8=@|66*9Ni*1nnp5ef75u)h1uj9i$!@wbuaC zI_*iQF!13h6sOFdad^A^-b>GY5qXmhK0yS|xsN0Y-@`yNS&NCk9X%|tl<86F#J?C~ z=B^7_rxiP3I0~v%Cx*nj11FXdFPLeux>@BB0T-e@{Tn=keQd2960EkFK#T-UG^i;uWnzuulidpLI3Z!rgQy7<WU6ly1PQss3K<@57ey!? ziM<q#Di z=DRDOU1PJKqFXlN9IY=Mer@k4%eXh17dSG%)a{&RweUOyI;tRXk3@VI`dFqr;_JzK zirKLKU1MxIIdo;nJ3cjeM0wZPoi6i`3#kHHkm+7k94#4VvICyCd-Y|yrl<+Jj}j3d zwgwZIWV(6@snaa!mShdA@`+0k@H{AObI9j)k3@9$G4LsMpbkDMQZ4US=2;-8G^^c=m9MK2b@Jic3T)Z?TlmOxK5K7=`^lRK?U4tXarl?>Ntd_dgY$ylk!$TCii<{t7% zivxAkZQNAe)r@#W(BqvnhA1_!kN~=;xs+Pw9pAu3m~&9sv4H%q{cj=76tw zZ>gu|?h{v}{*ufmX~AK{hdtoj8r&*lrEYs3sAH?Co%4?f|A_F97XF#Ye{A6|TPVR> zy`=He{DUpG=>hX!+eae9TY}Xou=gJ>C4I(I)VtOV?9 zj;fav-bt4Ftv{2U3Y`biC)P~Ti&?Coq!n;KN@nZ&?~xH-7#(4x5q1h(7$BQ?@y=_S zHw;_r_c~rLwHwHzLqG{6*e|~Pl6$#BFsI?{Q@)JcKG3a12`7agKmuQm_So1$f&&%z zX?TUJ=+n=At`=XuZ{&ULkZ-R0)^|?;kuVN(a9^D57$nW}Xi)lbDY7}8RMB4wl`qAd z_Ng-QI=-B+6|V*|6>fl?dtP`{v%P9WD7UQnTC4}s;>$17o$7d3WD)N>YVeBqUjx^h1<-dP_c7 zk6KoYbR4862-r1DMj=XJ@N1KYkjg@0!f!s#1pd2FImOZXhLi%g&Yg@-v851D%i#@m zN1zN63-wMLAND6$gGhjy4@J{6Rwk#^ztqmc`ezxP+d+@K<2l8EGN6GCy{pS|zI)&l zkm*rUpXM$Un)7&KQ`%;eT*Azkzo9E887R}3Gnlg8-AQlqMD+Ob`*bgI9g0>CmOJb- zmwAvd-IS#>vHaKrl;vy|u9qId`b?#f3$k<&t8HrUr17P(N-u=Ilz+tQbW*+#vkO4k z;QoWX(W81BerEJmIw|;X!89lqGRqjMfu@ zt#%Jw3#ZtI5q|{uM}U7c@Xr+fV*~%$iAedMIENR!)t%JqKfK6Pq~u^-7{yiy2zhCA zfpMq{3Yb+Lxz%N@Gu}Yp+M^^*WS|7^RjGP^FtmJ$2qYWj)@~Rf<(zj30@Z(?hrri^ z&ZNdZQiS3w2cooxa|jV~vgCw~b6NyQYG3Oe;3x~9F*lGlXQ9hVrvQ*I4<1b>MN9=K z-(>@jUXBC4f?-0b>XOVmS#x-4Td9U)P$9@_pRJx*saGBLAU@6sh`eF} zY|fk$k~&qFnjmR*U|$S~95d#80N+;IN9-2O zEfwK?@E|R=mr={Z@E$QVMs*flAu)`I#qMN$T9>7tRLa~G-&@l6(Uo#ko^zC?Z&2fl z0m5T_8C~=+f~Ov2?ZFz;xiFPwyKjj3(~LjT_@f(tX3GD~cEj4^#IQ2G{AuA{DMr{3 zN`A_s#C!AHQS8m)jBf|squkrcd0EnPbGX<8{&N1+Rq^z5eo{IGmniBVo(1ga3zRSG z*(prkE%f-Z7SZ-Xq0|lWbmIJJCoQ4{9RAfCkL@hra+-+h0y#3D?>N|*iSqZ0HW;y_-~q<;PtYA+7&btAO}5HBI;>N3i^a*?xkA!*p@q z5`l8~Y8ckb4dk_uiv@xrHAO|UTRK|GYB!XADnjtu+|A7iN(cbXE?yowD%Thd?-(&+ zEP~R|1B8GifMagy>85b&){RS^f383I|8+VX{V6*z#COT-FZBN$KxAd@X$dNS3naI& z^0agY;R*nNKd^N3@B#pIaS+eq>*aO{n}9H>2Phy2yIsPzzu*U#u*EO<8tl{eS-WUwLwN@&@hov#^6_Tssd{9q>vD z+HKs?RpS!I1>p@Bi?Obo(Edr+faFcjO)TU8H)L3${yrKcijOMo1NuUeU3*8pK! z5LR|^x_Q~(mv{>sdu25c21f|g&)!Q-7lbeCKD72!x(&i0f9PuuSDoK&L0{W=C@TD} zTVn13Lci$Sy&d%~@t6F^?X1=Q@ZS%x(=`C`pv*9AAA9{vkH80Ea!<$Gm-2uzz*qz9 z6ff~VX*|4jF6Czh@sidqD%U_5lm~X##zXm1_e**YXHO9NDI4seoxR#+T~JO~ikF@4 zWglPiDY7*GDL*9$*I2tK{I&_~uC=G(rEEX-va-Ie1j3;1uzqV7z2Ex6=G?rlUAA>u zch}A7r*DAtaBOQQl}kJ`2-AA{-2Pn`F6-r?dx;0-gx_@Yx^-zoP)@jogSqg!e0QS8b)f?ciUw4!NbHD?z1*`$S-Ys~_1|<@SFxa26j<_Dod2~Z zJ~zI|UlLy0^p`z;S?!ms7Ju3Gj|BhO{@-`(0V_~Wjo-5VMG1Qd`vB{LwZJ~X>S5IY zBdiHl1N#bVxP*VVul%zYZU1Ub=Vu=~f<0sZ7tJ4i%|Xm%o0sFj^J?MM)vKsqT)eFP zyufh_D7w1&d)V9Bc`+)0Gru*Xnv3NXUPi&I0ulgl`5d{V0f0U7pU(#f`RBiJ?#%!o z5ev>ums2?7B^3aevjD)yB>*5f{2Rxz3%bW+0BGp9^!D)iMF+pUpaIwb0q9Xw;B$%v z-~{y%1VjNTKps#6)B!C(7q|o51xKA7IG#NKUmyqw2cm#S;FI!gJhrlTW0>OabK}aA}5C#Yvga;xB5r@b^lpr@CI*>b% zdk|ZQ3&a}|1i24+1WAIVK(Zk3AZ3t`kQT@%$N=OUWFE2#*@c`yVNhHsDU=S%4&{f6 zLlvMJP+h1g)E4Rv4S+s?CO}_8-$09?RnQjb7w8Cd4!RCKfB`TZ7&(jy#s?FJDZ;d1 zMlc(g2P^~@3wsWG11o{mg8e)Mn}cn@j^XHV5;!BA4;;a&a6Pys+#MbQkAtVc^WjzS zcK8r{9*%&Yqv4^^qVb?fqN$-7qS>PPqD7%SN6SU4MC$;(=?B^&IyyQzItRKK=#6*K z9nb^O+s>SNV`hj(dO@z&XErYF# z?SLJQ{Svzby90X)dk+T(hZ#o#M;pf;Ck*E$&U>70oOzsMTw+{qTt!?HTyNY2+&tW7 z+;6yhczAg1c=C8gcwTr3c<=Dq@MiFk_$2sO@YV3G@I&!a@vHC$@wW)D2-pY|2<{RD z5IiUNK+s39L5M}jPN+y|P8dR%O8Aj*jPQVngh+r$i^!QMj;Mg>6VVDW1~EIa60sHW z1LADrHsVDR7zqoBB8er*eUcoK4w4_F=%gH^YNQUNk4TG2dr1*w#AL!``ec4&sbmdg zbL22`c5*dxC-Ow{a`I8~6AC&C1qy457>W{#L5d?v8cKOeYsy&4Qpyp^6Dme3B`Qa% zCsfr`)6{TkZfb36U+Q$~4(bgWQW_~5OPW}ka+(QRC@nXw4s9T9Htkp113G#-6*@P% zS9EQ38}#J#^7Ib$Pw5-!R~bkcWEt!ko-#BstTB=?Dlj@RzGQ4?L@?1ZsWN#pWijoG?#moiVYV6%v`*swfhX=OpMGO%j02C){hPOxFIiL=?UynBv6al;d>ge8V}+h0Z0;<;a!B)z1y(7U8zzPUY_7f%1s**z=_E z4Dh1yO7ObyzTq9?!{t-p^WiJxo4-PS<;InWE7e!F_*wal_@D51@t9+@ku#K6-cc}b4XiD=SVNg zFw2uHw-rXa)vXrljKIv??87~@dj|Ik?xD=J z%-@(FSg2XNwm?`aTE4Jcx01JdYPD)DW1VEZY$IcnWV2!`W1DQdYA0v++-}2O(LTj~ z$3fL0!{NwL%Q4UK!pXp?)EU$Hp7Tc+Vi!l3c2{~=Ki2^_9=9mBId@6-XYL3O4Ub$; zh^MJ%jTb2xuz&UD^p5nN_mTBU^+EdT`+o2v@^kU~>d);TEQpEm!gZs4)=pKYUSd3JOER4dB z@`xIXmWs}J2!Ck*us231=2gsjtX1siM^_&`e}sy&jQbof5dSj%BEcr1H&HY({W1Du zm&c<|47!X*87GXLJPeMSBp%F`irj@*Oc&;WR;SaK6($l_kO=t zcCT!-T%)}8gZPJ%3bu;Zl_Zt1RgfzGDnzwi^<2%Jnt_k%A6sjsYAfrm)aBJP)u%R) zH6%7-H$G^*XbNaLXm)GfXt8ZsY&C10Y%^>dZr5(_?a=7x?o{q<>r&`y>XzxQ|0Mb8 z<7e^D)n7!vRDBisTG=DgQ`sxpTh%AlSJN-iUppW@&^RbJ*fMl|sB>6t_{+%6k%3XY z(eW{pvAJ=p@s)2*-*zT^CQc{ArqHHhrwON@&(O}indO=-ofDmFn7=;%b>a5HlO@Rpu0Gc3Pb5nQQXRbK61GhX|#?zVoq5w%IQ`Fe|Ut72Pz`zyi_@ngqh z=VCW*k7_S(UwFUu;MT#+q4VMCQ7n=QnSU&P+;w7bvV7``!a%(|<2tK7*EpZLaK5;3 zGxsq684JL`Gt>smz<2Wi0PhYMJM;qpw(+k#?2_RU&gRAA)&tx5OY~PZG;4W0uWpeFf>74T>Qk2+`=NFV&W2#ir1Bt zRaDi~Z|mym8-O91rIodft)0Dtrnub+Rw{Rfdz(GO!{lail4d;a3(tCTm{Id60G z^4}Gdf2gRes;>E1+uGLN(b?7g>GR;w@W|-c__vAqg~jhbmX=pm*LHUI_74t^kjE#N zdO-l#FSY*C?7!$m1nLEa!(niYOT8daU+{nt!O@rm(1{haG0fdbm<7WzNv}Q0Dr>=F z5z;}BS$GU!ld}rXv+Z1}_EWR}ImIIWUupK2Vt?y335L(mp9Kbm!q8wa7#cbnSkN)S zXbT-36C3kq!Tw!ve-`}9Lhx6)0GU8Q9&k7u1N=vbgM~x*|F~RCgNsP!7ZU(33<3@& z7!e=`oT1*tasq!JH(~)JuDW+(4C+8I8NDWzYz2gkjA1k>jJ5k9n>jms<`Q_@oxjQn zy^&}n$SoMC*D5_QO*n zsYpMo|3v@fH^teVt8?oTO-4t*4`H8IRuTl;1JU^_N10e+*n(V7pZ-Lvs5Ja+#XHWOAFJN2%Mq#XPL%j9^9f|D5N;ZnUdjz_Rh~dy;?BY!9G74#=C|7!tIF6*zMmt|JvZ+aqz#XFZd4Y zIq$n>;z*s@;T;sr*dF)LoGyXB-My|+SEZ>xdmb=Qw=PfaYx|BgtAV@f0thf|D+caN zXY8ezwoT~;4`<~=n`QUsModJmY#nD}wVkIzv) zB*>MsoG_bqwb}@9cUgLAj5^9CUwXrzcme1E>c)dFjT^2RONSn;37&_Y>J ztd_C`?|Gu~p`+)m$)N$cR(*6$lJ+m|_Z?*7G};EZzS`EUpRsRr2JY@j;oI#=jSz@c zgVF8dPh*$SEj7Jufb{3SJ!O08ehjn0Fk$K5))!y5@66bN?X2vn$eq^$1A9^fOZvVk zLMmxp!jP;bA5xHS@1Eynken#&c{$7DF$gu#Tig0)3;R!bUNSY;Sn@5}7W!Anrp7IoJhop$(r4Mb(iRTTftUn`VocYtCw!jM@ zUYpocivXNljl!-(ZE(v6j$Hjlxp_Ps-qdFVRM9j@GpHBQr1-hEAu@4*|Ve`i3WKbwHWA&DhMHp@0`W?yiLiAS{X;<#NjJ&S>+(rhVTU(SfniF}I z;v8r%$5+HyI;x`}XTrr0512}pe30PXZLuMc@}U|JcKiJCgybMMbz1iV_CF8~GACIsOHu(P&jMig`t!4c`Aneeel32!jW zh?GFOfI#yTaUDUs_WW$j4BC!fcUk|z!`#|?>TQgEZ1*HL6ZDkD56SutYE7nIubq{Y z1Y7`X=W^r0j%#~|fqNcbUTjw;stB#+om;qv&as5<6&e%cmzJg2(IqDys zmihDZn*t=-i9Um7fudPa@AOS7-h4l0^UR$sVECJ(j_XLEL)ow9mx`pdverCt0yQ#i zT&}xNi;3?W02!^-V%qn+wA&RY%$MHMujV%HlOaa9G?I!;%xsDbrwcBCetmuvTn-iR zRP@qhhD`|EOOqAXf+@|g#orX5r8S!LE`ZFUUmDi$wVRRDW6c{UTbDQzDSBm+N5cBJ z?+<&SlhTez%DpiagMtR^AR+mm%)lG&qm= zt+we&=^2{E1<~fTeL-Pk0e*tv8 z^IiMhD>T0;{y}irZ8}lE#DQd-%ji{W*Ug0S{t-~@0-%38`^Si&iF7$?qox(VPAZ7g zX~{Nw>b=nWV1r2;aT8rHtZz6@|H)g?lIKx6rI?L()Hv20xBl<+{5o7#_YZr&2>IPT zL`^>pu?GBo8#E24^DmQ7{kB5G%3o%<9Gn0DvJz;;+P{tX-}dr9v;*DgU*#_y^WUDI zR{nR`|DJ;X_4$7%yt#j8d+>q(@1ySj%V_BK{k4pL1hIAb)7$do)gX@aPhk6YPvm#V zbs1h2q12`%N?$O^Om?~LE_&rtRCObm~7S#42C$spzkuK$~!B(41Hez z3N=4mh6}=xO9gUEY(y78wIayYe&JN_u5va>=wugHHlsJY zS90fM@JDc{BDnZAe6}kakuAJ<9yoj>pdWUpZW!18>(q#i6Tx{Uz#?X@_iEMjLznB# zN*$GTdF)-^QrTg%bB?2ogU;FGPo{;Rdi)RemhaYI041Vl4c{}dj>Zy#9CmuPA4rSg zl_YXJd*?7nY4O-QR)i+!abS3)nex3rgJRdxmluFN4%f7_L?Lj4P^PCYy=B>OTId1@ zd1*VAaPVVUFHZ|cVrZso3hDe}e&@~-c@)=OyaxK7oC!10q-xW|L}~4XZ9G4g8^Mkp z)DiZFZxL2cpWCxKC5H)aEho8QNvBBjUlPpT&~$7)iEtp#La;u3X7}BM=&{?%VuT=s zmKw1=aeH1RCWPHCz$Bf*`uU#PbA8d?1p&dYq{LM3>z?L!E}&yAm&w^QMbfh&ZL`*r z{IZ_&^cbnWG-io`^I+b?vi`hf(Iuru2}^I=y#Oq=#`pFDrxQ1~ODq~24nnO@3xmw~ z>po8&jGy@+`4$UjDUdFXrWRUaI)he(UzpZ;oy3%w=yR-%N1mo1Yy=kmDE-#>*(T3#i9oX_QTONX+kYIPvrj$x0ecyTQwGJCLDWmh}fR=!$<#R-}G}!zZUy zwTcEBhiOrXZE}abaU&&1tH~?;l=M9QYojaM-~qSp^x_gODzs<294Q(IHp((*J7 zjT?v@P4x;LNtC2L#ADPqxd3M226Wu7$97%gUqvzJdh^OCA2Viuu60Ou9X&GG-IbjU zdtZ?q9@eG7zPm z*mNtnMPx~vtH(zcS6R&HS^sJDr)ZgKWvWz$qj6F{j+Yi(%i3*2u1O9iUvgtA&RnfS z<*qJH@p0WVRSVK~m=&{W-0t3Y9$9Rn4)P3~XTWQ7%>MMl!Kglp@-3Ztyn$Caf!o!> zm9GR22Wba;9GeW*QhCuL_plo5&b{leOCP%Ao7_RNF5b&OY^>lNUqwzvK) z7-)W|jXy9Brpbq~w2R^4UR8a`x8$Cl;Wc(;B7TTVUzhRVSPMD2{V>%_zirg)>`7tj zx7ZWZbAdzZlDuV*O^P#2-AU*p0^zv7}=uQU>&)^|x z(=&Rp(e+RvDL&|~W2*g)&6?~vtK?3|cJj$cv7^agkkng~^?)B!KBDCIjrMPy^JB$7 zam*z(H6JOZQK!N4X@bkb-}47WWLso#W?CMd zyf|1og7nQv3)QYHuS(qb!7EIgHXUb1a6TLguU9MX$h4nf{O0MgAa3Nx9wkAS0N}5X z68q7VrR;C=ED%JBl#;K_pbc%~tOqcwuuaz~F_WrYd&czY+IyzxX^#BTXq3W2&+XO} z^e6M76Dspg#7Li(#V9(h8{PuSpGC8sV#?n}snHBc&l9L`qkIePXYrcsX7Ny}N!s*} z7K+K&M%T0lQU>KD43p31m|?QdIs-64hAlRK_(+t7PXSj;dp7}mF%DOr&bNY2?5z!{gS=R32>xIIQflw;pP-u{F zT1By0_o%dzBjX@jHSU5uuKD)_T{H>@RVu^x8@fxC6%5f$_!)E0GNS2QhgV)utE;g_ zk}S<)`qc0zHYL}~dJr&_pr1ehkEoWf(1u`7$&Z3@}Y$xz~T>xYuW9Jn8 z^}3JT^HxuUOz%u;M#ZWfscSe%lk6^^e@j#cT9DH_T`gur(h~C=$iPzJ_1Ji$7&2yVHkw*z{jTr`%aC(g75iN8uGkyp@U(~9VQv!Q zWV;-jp7Tw(6SGK+cAwGy$+hjG7AGSSU4Q3Y%B`*$%og|b)Wl*-luONy=-bmKCGe zod|VZZ|-vLZCK-=`z+ZI^7aD%gL>!CV*3!<1~IV^h8of80b!l%{a>D&5HXi1Yey-o z&^a!i*|yBmqD*%bThgUIB196SZTej;#SBDWAnKDRgz}%zqQfH-{pfB|COWLS{uRtCB(C_1eXw6mIdKPnL|#oc%+ri-WrpLdFn46b zVid)Z*^(!{!}s;clz>;*dt$*vX;h1c3PyToW74qkIopxh!KVvgpG2OX&HwZ^3cqY{ zQ^a3dnVM(wT!*cGQvkCAM>tT7vs;`O-M!Osq@H6j0~-mtI6V_0B%A)N#c6VozfK7T z_e!(i)w%gFamIb-M%A-eX@Pkgq?vzda1u`1&BH@G7b`CVZ`Bc>j&5-iy7BG0^byOs z;I}WL8s&oY5ig>`9w|ID?qy1R_$Dokw;(&RO{xS#KGTO3@-?K-tO6q}gaO4;aesl} zA;YQq!r_lW=JKgt%g7u~5^IvqDmg%%RSD>L9zD8<1_-P(Sqh39ulh-hDq zl1=Y z{`!W;|GQO!!p9romQwimJ{hTWg%kS(M0Sl_gM5|S1b0>P?T;Qca=mf>+?I7h=*KvB zzlDlyFLa}4t_h7HJV3NZa$?`nt^25l)t)JBLRnv>TLLRS%m+3m@-?J8u1a)94l$F) zqw!`{a(`#M%(?B$kC5A|mAdB=+w$~@r}}25hA503wvngXu7Wgtr8K1Gsoe@Kl+p=2 z92#eNA-G6i&4GHEV!y{U$fC`p`6Q}5)$hA6n7&`P?hTlGO}o{hacV4wG9(~5a|+!r zryfCwwe>~c4#FF(z;fajE`E~EoDCTBSOEDph_DYsNXT$J{z5ZFWiK?(#SGq#5TU_;ewgCzw#2J4y(WPp3p2*0T+> zvze=zr;xo&=pJN_2qt~3Wv(x0XGZyHuJryn%Sf@6z>F5g_-5>Dxm2&~@};#=S7e_p zWK`NDzqOx5l%N#N9m%S)>=$UnS)L1Hej)m@*-Zyyek|p)Zc5O_4tnU9g~T?wNc+9! zqtaqC4p-(+YH?y8o-m)e%hQci$JbLKNam}fyh>0Sv z3#fh|%WPt#N^H8M>ay8db#CBLrDqH;j3A9a25w;t)@rqvqOYf8gg!iR$z(&IZuLo- zJoTQWD>LU3D39*u^qWkclpwWhBCZE}8?LrPp*{?+g$rm&O;Jv(cR3lVs|!Zc_@oPf zRAk5F-WuDuK@vNq8{5`Q)M;I@nS(_Na$?{3X9H+My`Ph0oUCqfoBHad&WDD{at5K7 z+d4*%@-YrROEBH6G?8r%T;N26PB;LkyRLC&_{Bj4&O7u+h8kuBGq&C7R!^vPC`*W$ z-zZDm4kJxM5AZZOijcF*cwk28?cFaeQBt@jX!|9?FQ?kx{Q{8w8e7oS*zark+Rbs9 zfZ9(>{r;kDOKGIhTG2V(pjY)IZpNDJ#|SRj-E=IQSn2y|-?{>3&&q}4DO3@%F^hbH zlttcWJi1lJJP&nQ8|bQ_HLTFAunmKReaNFzebn6nW0L_{@(E<@vRVQUGNJLXkXwzc zFvq3F+C*(v)y!e*J8oQ@UMO{tA0o81^uampKnR}Wo~g;S+TkOA{>&tM)-o{)qOTi+ zoduXFqPH+HVBvApo1u1Z)nr+ND3T1H-8J?0O&~y3RL`X&-=2L~sMdOL63FXN=lw|G zG0@D##T+L41VPd6iix^0kh6nvpI`GJ?sjFJM69anGcv=HZ)933w2QQ3_d~{v-p;(5 zy8y8I?7a@_>eZ}D20kWo{kWmZO4LOqvCxWH zE`!ME`uKX~e45sZ9c<{{S^hc36f!(Wf+EIJs6Jiiv&BSZrujg*Z$}2PT^iL@ zrldw9FB3$}Sk9O^d%xlf#wi^1xS3VtHTd6siw{rjJ|zZp6bW&-4O#* zY|V@|oDAY1xzXyi7v*Zsc~pGt6MK`weBwN;_wdTT=JADjcB(ShZAWF$*OM(WL?6+! zz0eiXn(vXF33l$N&u4n}hL3$@Tr8*?rIP;Ir)YK}fW`|+u+Z>?dozmoD$pWEK2M${-MIhwHc{3isaTJ)k1wn z+XYa`(ZWx>?rulz`w%)cRg*v>O`=Gf&;?+4VPnaNSrh~*BB^rM&Cqg@8gqgv!i41- z4s5o5Uy2$Xu_(7Z=wg;%)6(9vX!P5`cqF2Qy+W-L2`--SN50pXpTtO@^p+7A?x10u zh%aY9*1$~?%k^)ie+pSM178f<^DqEGiip_<6%Bg#j5WqDfO$scDt}y`B+MM0qFdiz zeoNfi4fI}U*p+*97YK!P?FbxrMYknIad~sArdD-ZG{-+xH4b=VC9^CFi30Ob7B2V{mA7-F3-znTNK!L zV!z+>4|lIDH>T`V1=gkMPSu0xc6vWjM0?SBp0iW%RZ%WS8A^|C5T#v93gnWIiS&}a zikw7zX_g#9hIX7x*pIz);1`wFsWP|a6n(0U+kXE6l@snF8KSETPh~-A4ask~lNjz4 z#9TGeQ81z9>PqndWE**`=poLCFLbwQp^0Fnk?pM9OC#84P-=zG3*z?Vb5{Qs z+GI2~Eev}Jsx8Q-Je@)N1uzlVYUJCRywC)9c0`-p-y?>kk!!D{qw;Nr;DAHgQ`pkAlah3|m(2tFb>q?du#> zWuboWn8H~M=O5(j0asj@d#{R@UPs<6Q$e(~@)2`y5A5%%$Jw6I$w>BN~&u=t1QQ#$g|u3$XfkPp3Da`J4T>0V+-5cUPqP1>nSgLQ*et z0fh5i0H4T){e;@(Dk_&_Hk|7{=hLX$6n#P~`kbYNyR#@uT9!rCoM0^rVKRHBV-*hiYaCBi>J$jT%^=F$(ezUb|zWO=o*{-v*$iIRN9ml3cQj8#r}=&PPK#Z2Swc9 z%=kUSos0BIz{-ggl}WjN?c+gH_Bct4QdDDzXSYzYcRrOnsgAEG3MY>WCHro`un9g@85schwaqCtb9 zqD-Z)BZ$fJSfNm=@Q2*^y3R0vBgX2Nps(wW-rl%u387vLZm@Bos|@M~v#|lo?Q}S; zx7Qf(gblrW%nc#9gHBlVShjkfj~@`CH-EnA*Lzi7M$TJMZSXR^A%d`;;%f#8oO1h} zF4)G^J1^=Yll^_he4ZGE+q>nGR$1&?@lxJ;kdQ|uPQ~6hGMxI~B{;<*{z1z0zY`oT z%nqs)nbfCNwB(&&f>S9gjp$2gq;ZsGL)^`MaTxV^193^S+QzQ1urBdVbe?rbvxhTyjR@T)!VPcXj|kLNHIE~KLQ$57#K-btTPCz6Vz5pj zGs*Mr8Dc~qigOa@*lV0U!zA#7&!BRNjc&TC9*vvCt^BJE5*)OzkNI222ip#Pk$u^d zNqkQHA`j=Uc~H~($ovU#uW|8iNYC(wI8j{HjYwh zv}aMn-5M19hmS4ES&5SWN^V&GBe^-d8Q71Zd1|pHR_ojHs>JIwhVShun6i;Pbt0a! zc5}@8c$Oc6X+P18*qn{Iy(v)rjP((2AG90)s*Nt!7&4q&DrG*@nF3rTZB&96g?zO>wHJt}rY{gJxOI;nD+!ao;wNHQqsCqlZzVmPr<|{sR~T@)I(VfiF6O z3DkG^H%$?f>Z6;3Tm7p?f)zS=-96AL_0;gWur+)F3`$|hmMr>NRtQPZ^-=J_Nr$Ma zVjngXNZ>Mm4&zivvfLw6yl1tzeUBs#X4q7*99tSBfk?hpIY{?HcWmjtM#Z?#$D|*P z_i{XwsT*b1zslK}@@o!-@C4C~d7b)gmRgT7SNgNuDnh(6Kc;zxS zQuusGeqLaXvKB$(v>tcb&oR?<}WNu$S3-YA*CW&1?J|gQr0* zNXs3VE;4d{`%wk~W7#PGK8>Z*g!MVx13E~!Vpy8`#$5?bC0p3U}bj&dH@f(ioOo8x9&QYJ3`9I?ew?}Vxs~#M>m$ZnA`IU+MS>1?z?%$8HAq?8GSvG zdl*92%Wu>#dTM3#=M(GzSpypXU%Su^M#WtVE`oj#GuQpGCkF{WNj8_ zUsFl?i>6{bk@44$ohv`=2BPURQ-FhDlEc?Xt_1=j#rZU$cJ(n|LCh@MD7&mAWpV6s zNwiPWxR0@#;Y&4g6EufW(eqVhSx8rf;pelrDXMlIv>QICRo{F}*J1e1$?hEE{x2@ut03rP314L$%w(Vd&R7cOz&sf zabLgVkR65$2I-#V)-$~g;sMti3Y#Z7H&qPxEC-D=?x=s_D=I6RD|!e8_wq@@CPhYq z8t`*O7j4WMxjPF!bm1@&9l_dzF`DmM5#(|=ZOV}1AZR)QEw#%j zedh3m+$prw7aAFhw9mT&-zb@aaq}l`fsE$_H!C2sYfY8>&70I)GrfE5v3=6+UtSh3 zKhw6pwO*fNYu`d;R-YFI?A-mr5GySs7Tp!QI9xobd^I((%Ky=Ke@3gu^DxNpVJR+> zZ;l`Mg5Z%KyZ}BpyA)NX2~urqyeUhuU&Ise>Gb6x&RpgRLl?MfdCIX`MbC%iS>Q)s zFYxArJ5*O`^OQk0)5Tv$aw{+hJx5dh{@qia!H9qssZ`G!4?zDV=lAR~exb)Prly^G>b2h2PLFB3P z{9&K%`rALTy?>UrcqQq$h}H6Aw-0M_FFyWN0W)d8`yDBzk-~T^;F4^`i`H>A#OD1j zT*>s8YZmcO$&|m5SpS{vtjdvw+D~(|%u)z5;M`eT$4|86-C19AHk}Ziz*R6d=O{&e z=xYeZ$qzC@hQ9S<9$UDQSIAZs9u$kXxibHD220QI@Hp}zd=uw2GywZ}c46|4$$5V$ zhHV({ys&0%-B+X$AQe%h2(2M}-Xf*pym9h)IqXttawc5CnWjIGtLA&@`Z<`F| zk2IKzw>fQ_;Tj`vyET-R;BR4PW1(}y`CBEXk5u@?M@Fm10Bexh4!8ogKnE`GMH)4M zYh9M}Tus${77-eb`O7J~LW;E#(jVdr1mgBT(XE3INvSO4qr<+jNs|u=2VT?oH2LIu z_=Q(brOgl1v9LGe_N5c#Gx28QGAa)5XQQr`?+~P%P=_ z90dg62V`5bIOkk`1`E$4yybAsXcldMba>qmRIKs{e+U`z9Lw)6r8z6TR~0=cTf`JY zs3MczUw|&;_Nr6Trtu-7wT&U#tB*|&QW=KI#68~#xP_tF7(W6o4HF+UB^dd~r{C!ZK0jaV@N`U6N1zx2OIM7p*B)k5#qtD-f2h4<^Tg6* z6Fx`&sd+`pn}Gv$Wrv~jWvJatl+=!fA@9-2jTf7F#-Gk(oa^UVFl9}!%&J z_zre)doBQ~w-{%S!2P<{9Dt8IW*xGUJ7XQwa&xwAXBJun3V5ZH2f^27nZ4rnuisOk zC0kLx(|Hquf&TflYZj2@L0Yzg5mU?-%3z*=>f?u4jora1V^b?`;CmIs_9l{B>&r9r z669U4p-?c(q*M%=G}G^&b2OcDA$t4X+RW_0DRhoK`7kGqU-}LrL!rub|1^V8iPzlU z`nun<)NyF@IellCScn+1uk&QNT?-EdE>XX$dgNc&K2{r*MJa1VUSJHNycZEv#Q`Vw zxiW{AHc~3!Hq|8^+ zKC~+BRg)8*aa#wUuNUvz7(LmVm>z~I-Bs^-IZVHh>81tqe7}hC=Im|MlkNxk+fh$c z7UdaCOxI6DGxERLi;eY9&a5eQR1m8cvUUo5L{)O|Rd~1$#%q`jRQNm1jg)47NWS&b zc#?OJn=1F7>UB8ktNa|k9gt<+(OkEe>`j{753KWiROWk>B;L&U-U4F& zq?)E&AO*o6L6MBQj;zF+-Gq3$1>JdT)WRN@Yu-d`gelyvZjS^j-iJP;7*x=tG6&-Inv95 zFL&H`IOh8vN*fCy&93jrmf3+h_(tt~gLCeYUn#f5^&IA1?6y{TO@ca|WXsL`B99UuzV{2f))NJWU;$uK0Rc-*@X z+f$eHyCwJmw7y#Bq&kZtnyDCuEadn7vOGXn2y%;&$w9MFB z*A_V0({d=88HUhQ(~ju7?wtp78@N>7RHn0oa)55|dPIU@ja$v=!}kEdDk)$L=65_+ zDz_frw3k$eWwmZX_hs3%0hCJr0_ZgY>Ke=H6~K_0WAKWwj0L~s=nuEHEI4f;*T!dg z#zW=`v4PrLZJnF^mPTaD>hA9}C)B1zY8?w?MaLR3kh2;ey$7-rjVt=LzFDc-bY3ZQe;MHXuD@$Na=X$WX;Wlt7NLU5fNw&EkT26&*DR8_VgJ zNore~PTK{OE7N95l=*MT%8u~@_6j9*^S9ZzCa0_Gs6uU>-xg9p9%bGpLwf?hRPv(bv%u7-*j1Rr>g0r8qiyNCp9Y*dIx;E z=tFXnleFG(B-x*?Osila=uMNkq*={`tDmM#*EuEKjq#82Y&U%E{mk~wN)_)`kt?BH zfLYA`qcj4;N4=G`CRbM!Ft|Sl3O)vR4i)2pc6$Gsg|p_9snBx4V7zLLrl|~du=Y-4 z145*m9NCig(Jdbs3AE$6R>``rhScn}T>8seEut&gJ(3+kbqn7IaJS zyABw?>7Bcjtg%8IFW6P@)^MxjACv_4WSj@Ry#N|jz%@kEk`pn5tf;RAI*F`K{^DQL zpZV|g)PbLos*o&AF&ulOo*y%yO!-sgP{%jGZ)U*sx@63orZD@|rP}%|sMqYkm0772ylc58#qvD{Tuo@A@k_zs!&p#hubU2S^SK#+$XaRa zoBHMSjapErWuiWAFsAfa9+*9H<)JJQ&!VIBQFf)dQ>^_r-}|3xYiA2=%rd}Wk>R9}$)_ucjiSXXrfp6i4lw z@T8?25v`N+H!~xS)MHzmti{KzTTz4I$(4^#i||dC=-BPcZBMeY3?B&M7X}$xL^DV$ zF;Vo#TJKY>1s_uG5xiz);G$)C%5RoTYw%(H0>BW~*uFo6qC@O2s}9XfGbUIvq<^W> zdzSWst066%ulsxQ=H1HL_+d!md@;5h0rIvwq9W<6Sa_0?_iLn&{~O9t!AXzt>9|D> zIzh({S>hnvQCT9#7dSb7tqXgtgU2N)H%8ZY)!46|gRdsKtW1tdGmkDeW^I+?AsY}n zUlt;JpOvmo)SuJtut&9jTN7RX_)M*L%(UAO*4>6{bpVe&1m}uOg2xA1-#pvtxjznx zG|`|GYDX)JuWVtxd9HDbQI(l3oix%K5RQNUN(d!B;W#SGhoXFsOm%;|UPWE;hqHd0 z=bPlDgpa0Q(lf4hYFQ{8YJ!gif|PopQ*d@%Q`smXvE%R1=7KUzA z#SWseKAcyIhsc2c&$nLBFyzN9S|cUF1A1&TjCie6W9Rk{$LCs4&Z`-&^z3?dnNE#8 zLuSwWA%npzd{MdS?A;-_g+U{$v1u2T!LxCiWb>^q(DfAAOOdC3@*-H#os_~&&Doo} z2e&|PU|6s`9g`8Eu!RQ%u^{s1T;sjvB5T=>!q1s!{cN?yipd@8m=do|C*%SIeucd0ZfNC59s^rkeA5}jzpBYLO(w{*X6eyVNn5jY#z47S(V(&eGqS}_W;Xx3Ps7TIA zQldysgNT5DFytImK$7G^n7;+rH%{k}Zdf%_^y}tkV zSN&i8Ri~($+N@c#_u6Z9_v+RCbT`rC58kb_nW2*FK?0R!=;Ko(QtTXTk7k@@#L8JO4i8<#2htSI(E zWU~r*pZYsqdZ_Nvu&@2hBon&Gs>udsHCzvn8)2X6%e%g}m>92Q{+!l3brg|7^elVE zLG`6*;8=R78VNs6{!=r^ch)8e>i+)vG)i_nG@4)PcO6 z^!BdF*p$q7q8c|i`rQDjC2SYyLxGa>_Y%4aUFaogUnt1@RO+VogVF4RRetjmb1uJI zQ~GSKVp=?{!~snUt&th)Z0hJvbjQ%#klPs;matTSh!0;6+r1bUZj(03xgO+?OU*r= zOU_-8)eQE9+p*p8=OFKlj)O7}b>?~}M{_aWlMe=?LW>ChMB_*wvhhpaq1~A~uL$#H z+^7kHZB!UxoXM{Or3|NujgB_|pa6mFOSa)%Fu7G4A^4vASJ+*cCALiMqe7Av@}d19 z!rqFE?5i$$k;Zi+JI_7f&`bcvbU%?AsnO*LetTCoRSb8j{I+-WgY=K2gteVF@O+7G zY%jvS^{dh=LS}YuaoJ|*rpGE&2@@3N05a;f_XEs%h5$*-8tc4Eu70~qVifsBlc-Ov zMU3G%uGb-n@Fdd@^lN_4;-9pu^-{+2r%rPKs%bYsXqLc>BX`Pj7e??yNyg#W-{JAL z<+m*8OVF3F2bdyf1tb69Yi5A2hav>Z8p0ST(n6;EZ~8|rniDPqQ8=yID9dRl}7B#ajpc{091+;_bm4EHt} zN^nBFUOG=pw;A6lI!b{kR0dj5ZIkM^k&u{5;dOvLa9^}1`&KX>A19zGZlU=my^PR< z6E=q>*;k?MJ&s*7=1KLDwc*v0)JGD)UP$EBW<)Yr1DmJQZk#Bo(W=U1Wpzi@5=nCq zESc$G_GURS;DBk}ljZ|s9KKi^amF-PK{-{&YRWA@93r!Sgd`3%+I(Z#dGou5zH zq?r7^i}S27$yNU$v@v%!gI-o&8(&5&%+!h^(qljEN#QX1uUT>dg4$0^Q8*g+K8-4q zN^VBqg9c2%^1x=kcem+WvJoyfPxQ|HI4@sz#``cT*wkYx&r%oTiO(8_8vzI#qP|Yg zu=lyIj=>U*d10J(O!!zrY@0@KXMg^VN0Jb&In`vvxp}RN=k=W5@iIDf`vt?kE7=j{kIw#H3s*RQ|B@0qX&l@=E5ohU7JQ>)_E z8~MIK)e4*^H$ksX`F=pzd@p%(P8FN_DGT|>E-RX7OGp&?ia}-x!Ec0VQ)37JIK4a} z!JQ{5-J}lIxycEddb$cqt)LPx+IXweaU@6k?COjCtM1+~yS5pTR_}sVq;bIPUV#sumqs~y z5cr#pR3CC$IsuME`#Q1=X|c7`8v$P(_!Gun zGXg_s^0jg_&S|Lke2Sz|seIAadoc7CtqlM}=cSeyTOx9r{epm_$-M;nz<1ph zh;N;GmF5->^ooBzU`uEq*p9#f0lEEgBp^06H<7V5u4N9)x1R}SI^=4oh=H&{c_%73 z@s}2-ufaU0ESIz{JX`RSJCIdCG@pO|G)E$?~oqK|zP1 zH?@%KFojMq^CdU1Ee9Y@`3%v67dV&qMWe0gjbm1>GE6c&Jj=7Ywl2nOYdN~X*umoZ zD6s|O7WtviIdz8AEkSMP5+T0JuyNRQ`KZL^`u4T{eCsr0Phqun=E1e#;31iES8twp zD`~F(IpeMzv+irMMb4VbF1~17VaB=F2wyO^@fui&fushuP!LIR8bL`tGogD=-`!@> zUK%T0x3cLG0XFRW#Ocli)D5H(V+*|v< zExuZY9>|4DnqRTC8dDq+hu+*4wuMm?0azr+8E^DJs{5ws;lCvf-r^V^{>S zs!{0m_TA*gw^tt`PBUM>WlZiK2O%?L&dR8YyMxIorm6K9Q{?S!8uVG*D+({mff}8x+OI5=jwNZA729g#DMZS>&Tk`UI}LA zaUji;wsdy7pX4fN7b)@!K*+nQom>H z5nXK-4NI%ytLub(^MWz+y#d+`_+BjD;26@ffgj~Rr7B3eQwDiy9Gg>pBZRSDnhf5H zpm8@TFAskm!h#p{g*`vF0Vo25`6bUy1t*|;kF`_#RLFWSzJ+>uB=dj%b}>4eXRUjY2&>Fy5OE&F~K9(wbhmbJfpWXL9$iL;T3a zwLZ;+a+~`kNz(h_h>k)pzDcw`kSeit1-CgJ^X**rlK5Mi=dU6h2~FG&=D=BtwG2N_ zw85OIqpe_a<7sQ&28`I?o1s67csu+aT{UdE(&G>L9 zoj$wmqS!2?rhj|6%f}{KMaDbvW;B_r!VEl!qV+WVz!X%H)@g+RgzrLKNl9|cNLjo# z{LX>+^s2~y-A>~uf^6e@-=j5e?!~Z7yL(BNd&;4eu>GYT_gV2=josbCpayy(ys(Hx zrU+!zUQz@4!Kvr`NuQVeLV~zT@nf!nc93u5jR&d>vyM#>nW%wUj_ceTJ?H;0o&Hln zKrq4rUZMHhP_PSi8venYtI(gD?O;GZncdmCv>v`TK8PzW%r@$vnXI6YP-nt7c7*N) z(q}eET=lLl54KL{sY)s~+Fzi%H*kdu88EzXh7Wsb+faN9b|GdcSP<8JUfmd6Km3={ z57~V`DpM|EJYi10>jw_K&K7{04P(IG@_CpYpEthYWI=zwFZ1B7040T!tUOi$_wv+Z z42DOHhjSm@5x2Z@81Gcj-p0*nVF3Cpa;eLXTuSG# zbRYYRPU&{tl9N;acuNhRdrNSz^~pljwW!4x+x!W}_u^o{x#aVnZ_I~Tdw)k;?+X3g z7d*?O-kPj?v1o2^km^_aM?6{wpqg?p+^$iYLjd8hR^I;T+ZRAstcMXK={Pc#ZM+%^ zm~^$n=Bo}D(~SR(G3{S1)c!0c{C^DD;J&Q-emrzq2n04QZL$XfsK!u0Np|Iv?7w`YNEK-gmBY zGgf2?me!Hkeu#F#UCnFTyv!S8n7JYZtlJ}3YqoQmZUTGX}U+E6iLreI~fm}#tU4NvuGtw1XsaaI}j;tTGZTV@k+=4Awoukfn z^1RzkT~eUw`Raw^1-erI_$1C3a9qE#giXhwf#fv5KrO0Q{(!a76#~pR4{-R^6I+{o zLNz-kYJ6h)3set8uA-b_|FyqX#D-8Gw%ajz{MESR?q&U@Cc}U2teH_4eu$Cs-)>4F z_g{Kj0FDQc`G33R-$VF&A^s-E|H@>4bHv{q@i#~O%@Kd^l$w9*3)ec<-?Q+4JPR%h zVB#S=q%o16@$6JP=l)qzl83=|)F=B)B41YoY}#1Z3v73q$tk&wK!*eMV88zY9gILO z0ARqPtT7te{tEuSushpiySjv-G5}Rs;#xnBL<=NjZH!QNhv~&7@ub)Y zZio9-PP9M>FgA!)wNLOssrS)V=wsy!w4UVYXt}!<9 z#L(|Km+YU+6H7ltxL-cFF>%mv_3#2x`>ZbIrdT8pC;w~oYSAeXe(yX4K;NvQ@*{ya zI?uh48NR8zFJKT=HS)Ht7NnX`qN`p%g6J8J6jQlfU-fuX?xOid|9ULBA`^N*C2J-Z4**U2aUfE4ZiiEr3B1QLy0pHTtq!{6Pm{rmqP zPRK^>^mc7P=IE#`lixJ8JWUMpwrMhv`;8bEr&L~mz+HOJQI*$jj3@MB-WP`G9zBb} z@Y(Qf^$RtDI9eI|Q#s~`F>mEMdiXB7YhE5Qj>v8EqG&Yt>ixti79iNxuLmYIQQT2j z@hYPH>N-=q=4WT>G`Tp8hpPgB@A5lY{SRG9)1aQ*IIbLdx|2(0*_ZWR@~pe1cYAMd zji=2|$ataXVtbGmq?b$sUe6;XQg*{^xEAG0Dz`GLs$_5^LZT_krcAIc&kjm!c_i6(5OSgE8P6|KXpmVn5Ld=hU}$H6g(T?-`1atL*`7#SbLZ)rjWN% z-Iq~{E1}@J-7qidTF)aD5gRbazxTer+^q#?1d9v7nvj*k*WGAeZ~9@Il@owh(ujIZ zy14@06{Ab=Y5E*bNEL^>|NLrGfyXMgH7i+ZjDn>464^PiA|sRYG|vm?*9e}OVmtGkC6062_Xz!408+)6Y&KE**O zp|Yl(Ld?{RmII@YXn0+e?E^oA=xlauWY+vbcKR1ctj^V(!7JH>EeqtrE3`|o!7=6q zP$BC6w+%_hw4d6c&t_{Oxd@}Z0$6iI6S|`9G8ePv-IcWwb|Ef(**)7pU!i+%Lk5Db ztnc5|Qw2hQ^Qevrf~?-ZDPjYV$W*&wKtedHcn|dM2JFWdBRO%O`i_)$SG#?EI6zgf zIhd5j>oAh%>&&pkg2~ zm9YyAkm2{-@EWiRTmXPo?;)V}1kJkJpG7EWfNWMkDi&W(yR7RBSHOjBhPU?g^{@ZF z&HtmhX+^OhThr&K)gh&Z2U8Qhq}CrF?*wR=?mdP;`>&Vd6gDrnWx3b7IG+}fep)?u zT0hVJt(v!tnzZgs! zj!pjPwWF&Uc^QO8l6$*4vSVC*ik@SI12JB%fI4Yo2=-w<&6*!WT#CTkGU!+rd&eIo4h*&M z%KFJ9a?%so(?opH9!mm@m$X7nkpcC@Xp%L=7cYtag#j*Bn4n?dzC34&kUVGDfCybV zZ^PV6?Jv9e7rboN40Cy#c?rYnr&w zY}5hv&zs?g)%j)T*B3)}f8XZc%?%I(*93^tkf%*(Ab??Q6p?j z1-a=1{O#4B2U*J}p|F#JpVnZeZbRvJhsR&=iw{~9OD57GKk;JzP^0^= zrkzGFj|9{p%AUiO?4ME|V1m`#1mbyj_0;K+@q!O9S)E3Txrv9gx*KL>?~;1pUz+!In<(d1$G zK&JO>rEtVU7II3fk$9tzYNXE8Hnf>0C9I(p@;~n69-F5H^gPf)XcOY>N@eb@AacBk zPSE6|&5(0K#a|#V021BY|BvTp{`FjIzw(mmY8IQ99wA4#S;c1>JT%QKA{rSySaAxNF53cZoPq*xtsC-s#r_4h1LM+(IKQ=kgJX_5F#x6H! zSs_sHthJP8Sxs94^S9#)Nc?d=!z<8I8YK(mV*#)PYR_HXX*@tyBcCt>Mjq+X4WsxhIFj%7iI*gld42^3c!JGA?YZg~DAM!8Ea zL{QoxbqxA662N&P1fxRky;6XGytQGi9rYTVS%OvXy0p0J90PSNnc{^0qkH_H-8250 zO8LK*cr^JigK_jzo??9Jgxj69*}o{(_+L`2Q|gc@ePF>@&(w%3W`F%ZCaM09Nn-eK z#9p(?=$5+_(+OG>K}fT=I|xt7h;!i@Ii1>Np>2O!(#QUmK30?jht;s1)G-^_LUy~| z6&d`pl*?FCK23c^DwR`MS|De4*T^LpeoGoXQaD*um~eNZu%Sv3!wZ++zGU(t{N?~s z?z@c==Zdw!=A-bM;vNw%69k zMZRk&>2z+m3lme<#p@^Fy&cFB45GK(V)C$mc@=h79$FGZ5s@PuGa(tASu-j6pUI%e z$+)jI#)Xu;Yp^hCw?||{&hAIF4fO*r38L=t=5Cd4 z?4G>sVfWafcFwzQ7>I~;gH@YX4*c_V{xvt)b67j05~3+A+E6!{eO&Mg_-?93yMtU< zYX#J}k3H!Ntz>3;P5S@IL;s3ADg?gQIBGIRh3r33b1)T)&QZhzdN5Wq-8 zn(8P~@PRH7NJi`O=1@z|y862?AHhTfbqri}t*~^|&D7>MgY?+*bB*lQ8T*3fr1f!L zvnjIWJ9bYsaG3z4mbP^PX_B2u<%pkBS*aKx@Bb9`|AxOL+_S6Z2%na?%9>uD}?s;{Gxk^sEB0(*fXOh~Ci7A8vfJp1cRhRglLP-scr z?`iYYW{3OM?PmifMZ;_iv9cRKTWHCca>ef`DRb@T9)3JCP4Dt-%QCtJ5w|pZ$}oT3&A6~IjiH?oc4U< zCNl>5S7(u5BjhjhtzX|$PAU-E$?`nAV|*9SdD^nxg`<4Qwf;Y?yMb($ot-620A7$_ z5?mH2Ax{VO98%LYdX{SX?RoHq20%oK?zsxQDcjvZRNGAmg|bT6uC1AiH^bC#2nzZ- zRz*SOy=|Br4l4bA^o!4Z-;9(ogyEIL2q!yB8laNSZ+Oi9_T-cS31e_3Q_(P2WXIPB z9W7MPU$qkJkuJ=#LxVL!ZH0yo|Dj6#YwpUY778p}9W)(6zKIjiV!Tge&)f4zzFnZ( zc=9RFvcHlpbFf3XW6Ehmk1P&!|6AoW-f)g=%2oGt>3Y`1hKI2D1+h`B0_pCK?e@;B z63?u9>2H^56FD|MgmlSMe)lV|lVW@K=Ord~y^>-0?;Oqp+?#49$DiYEJC1HrK}4`tTL z2few=3BV?@`NB@`)TeQh5wiA1&GYwNR7M+(7xB^4jsa>04 zgX5`y>iQe~a1}vX{E_)-QY95-w>%fTi=8=hyczu~6C# zHk93`F~#EeruFhMDSdX8Axx&9zrcg(-KurwWO&oVcDm;(qMoA(Aw9VR&C+-~tK1#O z+Q?Z`5BHkm0NNLiB6b|tVz&_1bwB%lzf{h~L`nrM4iB9`0h;j-l!21EKMxDZ%PV9x z+{bmRZs4-58}d7`ld<=Nv3SUi8Wt7R6SXPP_5>)DWZarjL5^A%APmLm-~E1iS|4gQ zzX+pUTxMKNvl<*Y94p#YDoYm{Th!##Z54H0rL%q^%boH-G#bj{EpSP`GL~#6sxd8J zOH!A(zJ@e6{OuFeAo!N$TIz-t<=L*jB$~dfn8MqUR`b^JT-|F6L$xt(5#juU#;gv@ z9^C}5XB{c!-O=k4S3Mnx^cLc(t0qaSVK>fH#=;q;?Gj>%XogYSu(G93Tg5TIWA4g)IeO z=G1z8F5C#`eDs?b{``t=YPp|d+-TH0D*d;s)oW2lo1`7$f&Uqcz3}n- z-Raa1Oaql?_u^=8Ma4)6-H=Ri?(eeDC=h)VyG&yUEkGDY4NLQ-YZb0Nb^B!FvQbp) zyagrx%@&tD;Hwj$8ttiVIR)kU&wWQAAH*fdGg!KyA&Pf`H9vU|_IS%79iK;XV1+9d zuYuYTlEex91zK5@60fX}hYhVf#@>#E>kV5pZsHr4*AyWoh<~dm_b&T+x=xDQP)Q^_ zmxMDlalYga>VWrsl=X?7$d?(=C(r9mJ#_`;Lb_-(-hU8cuO9ZXD_AQx`<+S# zwqTVHuf9hTI9X1+)59Bbh`@BeKWmtXrgIeweNz@r2Nd5HD|eKBxEOL)vnrV3o56ni z!HjOyaA0yc^{s_`XY@quf26mX^9N?BQlz*8V$Pa3gU>P4492I&g(LQH-bG5?kClvS zff8kURN5g-#Q17|E5+tm$|?iM<-fWQqu0$C8M2|nN#0-&Zmx__493RzBTOzUkSW zS=2R;!2%gSG^*o6Xxc{D{zbEPUbo>7kN?PULB|nW(D2e#dq!Rt$e$VjFx3(;ioVPY0&{c~s zMD^&qyOgj#HRE>JkH}L<4S?>Dl9yxwqVX2jEie&4{G!Sv9)GM=AQqk;AYAnEkc%yAO1p`3lP({Z3q=TGhf zbOiuX`I2}Qxt9kR-{>O$grvLJI)#mFR~Gq9Esbp9WPxm-IC2=#H$~~}X%$MY?tLND ziKHDqy($1RX=iLdp-?%&lw6(2Y)9`N0D}znphcbk0+Z?dXr#BjZiD!#3PBTWjT}FU`AP9t5&+K;N6NX2lHI~#n83=a@{feO!!-WV9 zwS0=@JN}cKMl?3;QVyg8vMkT1b<)=wXg@-a5E}jqTJbhEEGhLLUWeQQGRl6u`8JI9 z9X&DGj%h`Zx}yubV>aBzWw6~Dj2Zd3OO~B9Be2O+URJ?iG-SC%3E`0gukz}G1j6KoG3BDHogVG;(dvpziceoOny`2H`{XT>cbpq^lcz|hCit7 z*nR2&^OEgPsm=9??137WtAyN*lV70M2QZ|*>nxvLd?$Uivd;<9ZMia*`gS;*ghZ8J)<5xp-;A%Ij#s#L%E{qyP|1<+Sr3nM~Zx>USjM)>2h1M zN0C%WAkwjTqkJQKPJu|{3KKgsA7q8z7xQ(qM1*sBBSqL;_8v?a zdQNP*ji+|LA`lgNUpltsg75t>`}vZkIi7S$(Ms!zK{TZ9rhWb0H~om$#kJq7$}6j> zBSYjStTg+2V>OAKhPU3V5o@RzACcZvW#;GUkapy)|>Mk7QZLHHMS=+?oJ;LX> zN$8&{yLh@#N$A;kE>7`Lk@ha+Eukl{f0sr#tw%nLx-b@$CJ7v zg|=L*Q+|X$1F#*C!A_~7sF8|ro)^d@>Aq4@*rAfXN`>V(@p+5y$9x^vm)Me`9d<#9 z4-ycIdt3L9Di81B#EbSC|Vs5w}t?{8| zSV!H%1Rkoyo-t2a%9kcuO1OA^x6;x^mP$Yf9cg8;T~eU7cnYFI_<{(}2bN{=(CQTL z)U2EtJ@;~@(t&$}EwP1b2$JnL8dJP%1*WD9a4#Ue)CtZ=?Woa^mnBi8C+8^C^tj}i znt7Qi*8^C*M6j^HyxlEFwNDmi`Tk!ewW)8K02#F6;+<4evI9-B^O%}oWxI4$R8>~xpT zlGxH|+rFVF(p7GX+~cnjKb~kl$Igy^bSlzF8cKsr(4DL;XxMu8R@g_FMYvTVdGUdO zgz3(p0!LKY_O3$D{kr##;e+glKl21R6}oC26#7@5oUNBL#Q1`y118(!k;96xvg(^PEc8KPs?&HHv`^~bi@B+ALup%r@57X2^V5S<|V zhlPNckGJ+bA(L&NjFToS+jyW7>5 zVj!?QygG~I7bt(f2Wql=aiP6HD;?zG5X;7n9$()bFR~%oM8s898w7f^`kQBUA1Br7 z{sK9@O|SW8-vh~E1OOgC;ca^lF^(360{Dlbg_$@jl)`lNtOi=2K`64r!6|=p9!12p{sut`x4CYHJiW?J zI_}nWo-{uo%jBpbFq6}|tb`kr8_P-TkLf$uy{^-HH^_uo9^A|96Tqiq9oavc70Pmb z`dQ*Ud6CZhLYrIuwQhvG2x}~@gu>XW+E=gA;S86Wv=*MK&a#D!4|)3Z8$*z|=u%s_ zhrq7lQem~NXI7H*v8RNdR_hv5BzT{!ZBvJyu_Q$J5YAFRtmrDwuoWlSU+^)$d7%Ak zQ)Z?ZHA#KVbn=!6t;zaKj3J6Pv8!w#)7aelZB*cNdtGS_-aA-}Ar3j2@eV~;37Ci+tjKKZSoCnpk<0K7 zPQv|;?_@ZjZ6=UQ8z$Lwls%d~G$xiCGF?%tz8ZTni!z51@2zl$WiKnnoY?YcoGrsb z(fC8VKrEy6HisH{oWeUzY2H`S&A|HRr4pG|mkv|Uy_Zko=>ZWCc?dLHLlMUA~3>a7)pXW5d|#w5XM<+&k5-)0J4xjlFs^Id~l+3hA;b`E^FtdguHzHOmpB!DD25@Lu(T-T1C<(#0vunMH;#<`X#wYc4bRUF5T;_6QnTo+FSLegC#6Tgz zuW~u#Gc2(SG0_bq@)Gt_w2>W^Jg3iVy6;|4@6Yy3NV>6JlC@1Q;s~D~E@XWhE&cg+ z9$7m`vYg(-z<4(rA`a7QMG%jodvz?;Y*kZsl8ptAW7J&T#&ZxqTg!=FOP0iuYv_M^ zGd&o(GBaSM+;hQybY`J9dT(fPv@jyOo#`&6wv=y!q{C*fz{G}L?QnMOtDNR9E%soi z2xWO%23zE6`NUGyO0hX#+cL9l2v8)6^RfI%(WEhoKop!s>NpN>!u8Ks!d7ec!)Tma zM0zA-8yZ3IpTfx=?n9=PnA7AYl^B*hbj$REVt%~Z=u=B8Nvlef2S=7AN_M7yKE4}C zU2QqZWCr04CCJ|~%$&OuEq_#$-x9Z6R9M8Lb(%YQ|1BPc2$qAotjho-g5a#;&2h4F(ujG8*Cwn`DGz^KW=2^K$ z>rTHnb;Wx5vWUoD1noi;S~O)*&cm<39ARAdeo=T$YD>mw*h|XH^UZM?6snh?!&kM) zh9lvN$nntBOz5dqpzoJ&i__$N$$@a3TsxXI6cVd` zzBpKl&y1`NY{}Gc+y_aM8>D7BU2%LyWhT0G=uJ&RNC(3TK9A9c*+0iRDZC&iEfl2)Y zNpKv`P6eQ;=g!mg+)7RERaB8995U+%NtRV^>TWqYsw!t@ttpqA5! z9yb2DP}9tENNId0S}Wv?%Q(qAFTAPuf`cNo5H1>AEit54s@-mNIVt8Xrk@9+h@VqzB9orb->0)G#^!@^z8KhwgD02r-J3G^0N4mww{-duLNqD25 zoD`WWj79*tFf3jjD2*|>ZrS`~$|i!2`UlQ;DZSv*q!!Qj>FKW;jhh;nMa=Z3Hre;E zXOnynpx2Z%vzWH-w6(O$I@!5WMrc;YXzjh8oyJ(%#bJDDDqVUM~TJ?0mc-{;P7*Cd;`nA2EP^T-`MzI>Y}ke%kE z!TCK8XA}!F@<-miMO|B?ph*sJ>CvJ2O=qJE{iSvd6MIb54gh z(iW7|M)nmwB}7WhE#9g7J$7hsY&^c)oN8Fs33%VMd}Lxe>A3Mpy(;{8cpPeQ^8GT0 z{_8A3EYp}Tq8U=~)O=D{S)Y!4GXRMN0=i;H)%bn7>q zl&8TY?~lcD3N%6|Ab19O)FCyBdG{#rzW9ar1!6&|*C$)z;*LwCvn)HU4#LO@2WKg_ zQy-$ZAwdOU0bUZ|H_>|g6}#SPHHo`)#|)&V6^UaLfJVsuCR*F82uRWqIiqCfzFVA= zF_M!XW zIpfY`>AQ%}B3V@^{&GEPi*9nf0Boi4pXyPq;FL{&vX3i|Ta@phv9go2^|FUDw|&C( z>vGaU=eIg^Dt|CE#6Fv-8WzlAvp%$%J`znz8W$Iv2OO~iq~aI&yi{1kyltt};bwFm*D<-iT3n{y^tsC8y;V;N{9&c?=B(PdzPI?X z*rvCWO3aUvjl$03ph?g+>4!sHfNOmxr3L? zy8wc8USg<@w9{KH82fo@8%q&%T&kuPQ#R40$fF34v}T+S(J*4s{2Ki!rBv_FHeHl! zIV`KTM(Zuv8?EV[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

          o(f!JOi#*7uSUWS6ZT!$GlFd7ZgBFGokh5nRW+R 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 f4mQny!-0-msDK))b5Yd!KoX* Date: Sun, 18 Aug 2019 16:57:42 +0800 Subject: [PATCH 521/643] up: update readme image --- public/image/start-http-server.jpg | Bin 113531 -> 104461 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/image/start-http-server.jpg b/public/image/start-http-server.jpg index 19810bb920954b12d84f844c0f7802d1d0a72c73..c596bc6d0d67882dac47b6a7e484bf62342feb0a 100644 GIT binary patch literal 104461 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

          GVY~;_@y3kO=ll5Ygl{O2 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 literal 113531 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

          #&PeMLkLP}j0iSz6>VZ(AThW95TQ}Js~cY;Fn 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

          mu!%J@+^?eC_C{w>QVilBPjSO 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 Date: Wed, 28 Aug 2019 19:14:51 +0800 Subject: [PATCH 522/643] format codes, remove some invalid comments --- app/Http/Controller/BeanController.php | 12 ++-------- app/Http/Controller/BreakerController.php | 3 +-- app/Http/Controller/CoController.php | 9 ++++---- app/Http/Controller/DbBuilderController.php | 6 ++--- app/Http/Controller/DbModelController.php | 8 +++---- .../Controller/DbTransactionController.php | 3 +-- app/Http/Controller/ExceptionController.php | 3 ++- app/Http/Controller/HomeController.php | 23 +++++++++++++------ app/Http/Controller/LogController.php | 9 +------- app/Http/Controller/RedisController.php | 13 +++++------ app/Http/Controller/RespController.php | 6 ++--- app/Http/Controller/RpcController.php | 3 +-- app/Http/Controller/SelectDbController.php | 5 ++-- app/Http/Controller/TaskController.php | 11 ++++----- app/Http/Controller/TimerController.php | 7 +++--- app/Http/Controller/ValidatorController.php | 8 +++---- app/Http/Controller/ViewController.php | 5 ---- app/Http/Middleware/FavIconMiddleware.php | 6 +++-- app/Listener/DeregisterServiceListener.php | 4 +--- app/Listener/RanListener.php | 3 +-- app/Listener/Test/ShutDownListener.php | 7 +++--- 21 files changed, 67 insertions(+), 87 deletions(-) diff --git a/app/Http/Controller/BeanController.php b/app/Http/Controller/BeanController.php index c7b6cd27..1f7f7efd 100644 --- a/app/Http/Controller/BeanController.php +++ b/app/Http/Controller/BeanController.php @@ -1,14 +1,10 @@ getData(); } /** * @return array - * @throws ContainerException - * @throws ReflectionException * * @RequestMapping() */ @@ -53,4 +45,4 @@ public function requestClass(): array $request = BeanFactory::getRequestBean(RequestBeanTwo::class, $id); return $request->getData(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/BreakerController.php b/app/Http/Controller/BreakerController.php index 96373323..4c7b81e6 100644 --- a/app/Http/Controller/BreakerController.php +++ b/app/Http/Controller/BreakerController.php @@ -1,6 +1,5 @@ logic->unFallback(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/CoController.php b/app/Http/Controller/CoController.php index f78912cc..dde0e358 100644 --- a/app/Http/Controller/CoController.php +++ b/app/Http/Controller/CoController.php @@ -11,6 +11,7 @@ use Swoft\Redis\Redis; use Swoole\Coroutine\Http\Client; use Throwable; +use function random_int; /** * Class CoController @@ -43,9 +44,7 @@ public function multi(): array } ]; - $response = Co::multi($requests); - - return $response; + return Co::multi($requests); } /** @@ -65,7 +64,7 @@ public static function getUser(): array public function addUser(): array { $user = User::new(); - $user->setAge(mt_rand(1, 100)); + $user->setAge(random_int(1, 100)); $user->setUserDesc('desc'); // Save result @@ -73,4 +72,4 @@ public function addUser(): array return [$result, $user->getId()]; } -} \ No newline at end of file +} diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php index d0ec77bc..49374f95 100644 --- a/app/Http/Controller/DbBuilderController.php +++ b/app/Http/Controller/DbBuilderController.php @@ -1,10 +1,9 @@ increments('id'); diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 6f5fc97c..e7f06969 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -1,6 +1,5 @@ setAge(mt_rand(1, 100)); + $user->setAge(random_int(1, 100)); $user->setUserDesc('desc'); $user->save(); @@ -99,7 +99,7 @@ public function delete(): array public function getId(): int { $user = new User(); - $user->setAge(mt_rand(1, 100)); + $user->setAge(random_int(1, 100)); $user->setUserDesc('desc'); $user->save(); @@ -113,7 +113,7 @@ public function getId(): int * @return array * @throws Throwable */ - public function batchUpdate() + public function batchUpdate(): array { // User::truncate(); User::updateOrCreate(['id' => 1], ['age' => 23]); diff --git a/app/Http/Controller/DbTransactionController.php b/app/Http/Controller/DbTransactionController.php index 048689a9..b1560c41 100644 --- a/app/Http/Controller/DbTransactionController.php +++ b/app/Http/Controller/DbTransactionController.php @@ -1,6 +1,5 @@ getId(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/ExceptionController.php b/app/Http/Controller/ExceptionController.php index 0fe8f281..7a1ee974 100644 --- a/app/Http/Controller/ExceptionController.php +++ b/app/Http/Controller/ExceptionController.php @@ -1,4 +1,5 @@ -render('home/index'); - return Context::mustGet()->getResponse()->withContentType(ContentType::HTML)->withContent($content); + return context()->getResponse()->withContentType(ContentType::HTML)->withContent($content); + } + + /** + * @RequestMapping("/hi") + * + * @return Response + * @throws SwoftException + */ + public function hi(): Response + { + return context()->getResponse()->withContent('hi'); } /** @@ -37,11 +47,10 @@ public function index(): Response * @param string $name * * @return Response - * @throws ReflectionException - * @throws ContainerException + * @throws SwoftException */ public function hello(string $name): Response { - return Context::mustGet()->getResponse()->withContent('Hello' . ($name === '' ? '' : ", {$name}")); + return context()->getResponse()->withContent('Hello' . ($name === '' ? '' : ", {$name}")); } } diff --git a/app/Http/Controller/LogController.php b/app/Http/Controller/LogController.php index 121d4f5a..20c11b10 100644 --- a/app/Http/Controller/LogController.php +++ b/app/Http/Controller/LogController.php @@ -1,10 +1,7 @@ getResponse(); + $resp = context()->getResponse(); return $resp->setCookie('c-name', 'c-value')->withData(['hello']); } diff --git a/app/Http/Controller/RpcController.php b/app/Http/Controller/RpcController.php index 18cd0e04..bb80f291 100644 --- a/app/Http/Controller/RpcController.php +++ b/app/Http/Controller/RpcController.php @@ -1,6 +1,5 @@ uniqid(), 'password' => md5(uniqid()), - 'age' => mt_rand(1, 100), + 'age' => random_int(1, 100), 'user_desc' => 'u desc', 'foo' => 'bar' ] @@ -257,4 +256,4 @@ public function getId(): int return $user->getId(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/TaskController.php b/app/Http/Controller/TaskController.php index 0168e143..116d91cb 100644 --- a/app/Http/Controller/TaskController.php +++ b/app/Http/Controller/TaskController.php @@ -1,6 +1,5 @@ setAge(mt_rand(1, 100)); + $user->setAge(random_int(1, 100)); $user->setUserDesc('desc'); $user->save(); @@ -59,7 +60,7 @@ public function tick(): array { Timer::tick(3 * 1000, function (int $timerId) { $user = new User(); - $user->setAge(mt_rand(1, 100)); + $user->setAge(random_int(1, 100)); $user->setUserDesc('desc'); $user->save(); @@ -76,4 +77,4 @@ public function tick(): array return ['tick']; } -} \ No newline at end of file +} diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php index 213e3143..43b7ff10 100644 --- a/app/Http/Controller/ValidatorController.php +++ b/app/Http/Controller/ValidatorController.php @@ -24,7 +24,7 @@ class ValidatorController * * @return array */ - function validateAll(Request $request): array + public function validateAll(Request $request): array { return $request->getParsedBody(); } @@ -39,7 +39,7 @@ function validateAll(Request $request): array * * @return array */ - function validateType(Request $request): array + public function validateType(Request $request): array { return $request->getParsedBody(); } @@ -54,7 +54,7 @@ function validateType(Request $request): array * * @return array */ - function validatePassword(Request $request): array + public function validatePassword(Request $request): array { return $request->getParsedBody(); } @@ -69,7 +69,7 @@ function validatePassword(Request $request): array * * @return array */ - function validateCustomer(Request $request): array + public function validateCustomer(Request $request): array { return $request->getParsedBody(); } diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php index 56797e4c..6a292d20 100644 --- a/app/Http/Controller/ViewController.php +++ b/app/Http/Controller/ViewController.php @@ -1,10 +1,7 @@ agent->deregisterService('swoft'); } -} \ No newline at end of file +} diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index c1c5bcea..0fc2eb47 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -1,6 +1,5 @@ getRawSql($querySql, $bindings, $connection); -// output()->info($rawSql); + // output()->info($rawSql); } /** diff --git a/app/Listener/Test/ShutDownListener.php b/app/Listener/Test/ShutDownListener.php index f7515cad..77f1870d 100644 --- a/app/Listener/Test/ShutDownListener.php +++ b/app/Listener/Test/ShutDownListener.php @@ -1,12 +1,11 @@ Date: Sat, 31 Aug 2019 00:14:49 +0800 Subject: [PATCH 523/643] Add files via upload --- public/image/swoft-logo-mdl.png | Bin 0 -> 99340 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/image/swoft-logo-mdl.png diff --git a/public/image/swoft-logo-mdl.png b/public/image/swoft-logo-mdl.png new file mode 100644 index 0000000000000000000000000000000000000000..0e27cf0d216b1bd1876e2b4ff336a64d6d20e3c7 GIT binary patch literal 99340 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 Date: Sat, 31 Aug 2019 00:17:49 +0800 Subject: [PATCH 524/643] Update README.md --- README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index ffba7b46..79d27fc9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -

          - - swoft - -

          +![swoft-logo](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/swoft-logo-mdl.png) [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/swoft.svg)](https://packagist.org/packages/swoft/swoft) [![Build Status](https://travis-ci.org/swoft-cloud/swoft.svg?branch=master)](https://travis-ci.org/swoft-cloud/swoft) @@ -15,7 +11,7 @@ ![start-http-server](https://raw.githubusercontent.com/swoft-cloud/swoft/master/public/image/start-http-server.jpg) -PHP microservices coroutine framework +PHP microservice coroutine framework > **[中文说明](README.zh-CN.md)** @@ -82,31 +78,31 @@ composer create-project swoft/swoft swoft ## Start -- Http server +- Http Server ```bash [root@swoft swoft]# php bin/swoft http:start ``` -- WebSocket server +- WebSocket Server ```bash [root@swoft swoft]# php bin/swoft ws:start ``` -- RPC server +- RPC Server ```bash [root@swoft swoft]# php bin/swoft rpc:start ``` -- TCP server +- TCP Server ```bash [root@swoft swoft]# php bin/swoft tcp:start ``` -- Process pool +- Process Pool ```bash [root@swoft swoft]# php bin/swoft process:start From 414e9aa49ac447808cf72d0df6b4dd08b852da8d Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 7 Sep 2019 20:35:32 +0800 Subject: [PATCH 525/643] update: upgrade swoole to 4.4.5 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index cc9ce642..05ae0362 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.4.3 \ + SWOOLE_VERSION=4.4.5 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From 03bd31b41318d715af38a2dee494e298c6d9d301 Mon Sep 17 00:00:00 2001 From: wvfeng Date: Thu, 12 Sep 2019 09:21:33 +0800 Subject: [PATCH 526/643] Fixing the use of validation rules (\App\Validator\Rule\AlphaDashRule) will cause the Request to get data error --- app/Validator/Rule/AlphaDashRule.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Validator/Rule/AlphaDashRule.php b/app/Validator/Rule/AlphaDashRule.php index 5a920095..a815fe76 100644 --- a/app/Validator/Rule/AlphaDashRule.php +++ b/app/Validator/Rule/AlphaDashRule.php @@ -33,7 +33,7 @@ public function validate(array $data, string $propertyName, $item, $default = nu $rule = '/^[A-Za-z0-9\-\_]+$/'; if (preg_match($rule, $data[$propertyName])) { - return [$data]; + return $data; } $message = (empty($message)) ? sprintf('%s must be a email', $propertyName) : $message; From c7ba0c05b0af85ff40aebd1aa1f1c15b5722b7df Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 13 Sep 2019 18:10:40 +0800 Subject: [PATCH 527/643] Modify demo --- app/Crontab/CronTask.php | 3 +++ app/Listener/Test/ShutDownListener.php | 5 ++++- app/Listener/Test/StartListener.php | 5 ++++- app/Listener/Test/TaskProcessListener.php | 2 +- app/Listener/Test/WorkerStartListener.php | 5 ++++- app/Listener/Test/WorkerStopListener.php | 5 ++++- app/bean.php | 13 +++++++------ 7 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/Crontab/CronTask.php b/app/Crontab/CronTask.php index 84dc982b..0df57685 100644 --- a/app/Crontab/CronTask.php +++ b/app/Crontab/CronTask.php @@ -3,6 +3,7 @@ namespace App\Crontab; use App\Model\Entity\User; +use Exception; use Swoft\Crontab\Annotaion\Mapping\Cron; use Swoft\Crontab\Annotaion\Mapping\Scheduled; use Swoft\Log\Helper\CLog; @@ -20,6 +21,8 @@ class CronTask { /** * @Cron("* * * * * *") + * + * @throws Exception */ public function secondTask() { diff --git a/app/Listener/Test/ShutDownListener.php b/app/Listener/Test/ShutDownListener.php index f7515cad..56cd6f4b 100644 --- a/app/Listener/Test/ShutDownListener.php +++ b/app/Listener/Test/ShutDownListener.php @@ -7,6 +7,7 @@ use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; +use Swoft\Exception\SwoftException; use Swoft\Log\Helper\CLog; use Swoft\Server\SwooleEvent; @@ -21,11 +22,13 @@ class ShutDownListener implements EventHandlerInterface { /** * @param EventInterface $event + * + * @throws SwoftException */ public function handle(EventInterface $event): void { $context = context(); - CLog::info(' Shut down context=' . get_class($context)); + CLog::debug(' Shut down context=' . get_class($context)); } } \ No newline at end of file diff --git a/app/Listener/Test/StartListener.php b/app/Listener/Test/StartListener.php index 6c7f5377..b6a51316 100644 --- a/app/Listener/Test/StartListener.php +++ b/app/Listener/Test/StartListener.php @@ -7,6 +7,7 @@ use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; +use Swoft\Exception\SwoftException; use Swoft\Log\Helper\CLog; use Swoft\Server\SwooleEvent; @@ -21,11 +22,13 @@ class StartListener implements EventHandlerInterface { /** * @param EventInterface $event + * + * @throws SwoftException */ public function handle(EventInterface $event): void { $context = context(); - CLog::info('Start context=' . get_class($context)); + CLog::debug('Start context=' . get_class($context)); } } \ No newline at end of file diff --git a/app/Listener/Test/TaskProcessListener.php b/app/Listener/Test/TaskProcessListener.php index 3abb46b5..74d3e73a 100644 --- a/app/Listener/Test/TaskProcessListener.php +++ b/app/Listener/Test/TaskProcessListener.php @@ -24,6 +24,6 @@ class TaskProcessListener implements EventHandlerInterface */ public function handle(EventInterface $event): void { - CLog::info('Task worker start'); + CLog::debug('Task worker start'); } } \ No newline at end of file diff --git a/app/Listener/Test/WorkerStartListener.php b/app/Listener/Test/WorkerStartListener.php index 97b6ee5a..3943860e 100644 --- a/app/Listener/Test/WorkerStartListener.php +++ b/app/Listener/Test/WorkerStartListener.php @@ -6,6 +6,7 @@ use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; +use Swoft\Exception\SwoftException; use Swoft\Log\Helper\CLog; use Swoft\Server\ServerEvent; @@ -20,11 +21,13 @@ class WorkerStartListener implements EventHandlerInterface { /** * @param EventInterface $event + * + * @throws SwoftException */ public function handle(EventInterface $event): void { $context = context(); - CLog::info('Worker Start context=' . get_class($context)); + CLog::debug('Worker Start context=' . get_class($context)); } } \ No newline at end of file diff --git a/app/Listener/Test/WorkerStopListener.php b/app/Listener/Test/WorkerStopListener.php index 397192e5..196f67ae 100644 --- a/app/Listener/Test/WorkerStopListener.php +++ b/app/Listener/Test/WorkerStopListener.php @@ -7,6 +7,7 @@ use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; +use Swoft\Exception\SwoftException; use Swoft\Log\Helper\CLog; use Swoft\Server\SwooleEvent; @@ -21,11 +22,13 @@ class WorkerStopListener implements EventHandlerInterface { /** * @param EventInterface $event + * + * @throws SwoftException */ public function handle(EventInterface $event): void { $context = context(); - CLog::info('Worker Start context=' . get_class($context)); + CLog::debug('Worker Start context=' . get_class($context)); } } \ No newline at end of file diff --git a/app/bean.php b/app/bean.php index b0040253..924704c0 100644 --- a/app/bean.php +++ b/app/bean.php @@ -45,9 +45,10 @@ SwooleEvent::FINISH => bean(FinishListener::class) ], /* @see HttpServer::$setting */ - 'setting' => [ + 'setting' => [ 'task_worker_num' => 12, - 'task_enable_coroutine' => true + 'task_enable_coroutine' => true, + 'worker_num' => 6 ] ], 'httpDispatcher' => [ @@ -73,11 +74,11 @@ 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', - 'dbSelector' => bean(DbSelector::class) +// 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, - 'database' => bean('db2') + 'database' => bean('db2'), ], 'db3' => [ 'class' => Database::class, @@ -115,7 +116,7 @@ ], 'user.pool' => [ 'class' => ServicePool::class, - 'client' => bean('user') + 'client' => bean('user'), ], 'rpcServer' => [ 'class' => ServiceServer::class, From 0b341de2a17af00e74d64f5e174819d1b27b2922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Fri, 13 Sep 2019 15:25:32 +0000 Subject: [PATCH 528/643] Fix all stlye issues --- app/Annotation/Mapping/AlphaDash.php | 8 +++++ app/Annotation/Parser/AlphaDashParser.php | 8 +++++ app/Application.php | 8 +++++ app/Aspect/AnnotationAspect.php | 12 ++++++-- app/AutoLoader.php | 8 +++++ app/Common/DbSelector.php | 12 ++++++-- app/Common/RpcProvider.php | 12 ++++++-- app/Console/Command/AgentCommand.php | 12 ++++++-- app/Console/Command/DemoCommand.php | 8 +++++ app/Console/Command/TestCommand.php | 8 +++++ app/Crontab/CronTask.php | 21 ++++++++----- app/Exception/ApiException.php | 9 +++++- app/Exception/Handler/ApiExceptionHandler.php | 8 +++++ .../Handler/HttpExceptionHandler.php | 8 +++++ app/Exception/Handler/RpcExceptionHandler.php | 13 +++++--- .../Handler/WsHandshakeExceptionHandler.php | 13 +++++++- .../Handler/WsMessageExceptionHandler.php | 8 +++++ app/Helper/Functions.php | 7 ++++- app/Http/Controller/BeanController.php | 11 +++++-- app/Http/Controller/BreakerController.php | 11 +++++-- app/Http/Controller/CoController.php | 11 +++++-- app/Http/Controller/DbBuilderController.php | 10 +++++-- app/Http/Controller/DbModelController.php | 9 +++++- .../Controller/DbTransactionController.php | 11 +++++-- app/Http/Controller/ExceptionController.php | 8 +++++ app/Http/Controller/HomeController.php | 8 +++++ app/Http/Controller/LimiterController.php | 9 +++++- app/Http/Controller/LogController.php | 11 +++++-- app/Http/Controller/RedisController.php | 11 +++++-- app/Http/Controller/RespController.php | 9 +++++- app/Http/Controller/RpcController.php | 11 +++++-- app/Http/Controller/SelectDbController.php | 21 ++++++++----- app/Http/Controller/TaskController.php | 11 +++++-- app/Http/Controller/TimerController.php | 15 +++++++--- app/Http/Controller/ValidatorController.php | 16 +++++++--- app/Http/Controller/ViewController.php | 9 +++++- app/Http/Middleware/FavIconMiddleware.php | 8 +++++ app/Listener/DeregisterServiceListener.php | 14 +++++---- app/Listener/ModelSavedListener.php | 9 +++++- app/Listener/RanListener.php | 10 +++++-- app/Listener/RegisterServiceListener.php | 15 ++++++---- app/Listener/Test/ShutDownListener.php | 12 ++++++-- app/Listener/Test/StartListener.php | 12 ++++++-- app/Listener/Test/TaskProcessListener.php | 12 ++++++-- app/Listener/Test/WorkerStartListener.php | 11 +++++-- app/Listener/Test/WorkerStopListener.php | 12 ++++++-- app/Listener/UserSavingListener.php | 9 +++++- app/Migration/AddMsg.php | 11 +++++-- app/Migration/AddUser.php | 10 +++++-- app/Migration/Message.php | 10 +++++-- app/Model/Dao/UserDao.php | 13 +++++--- app/Model/Data/UserData.php | 13 +++++--- app/Model/Entity/Count.php | 13 +++++--- app/Model/Entity/Count2.php | 12 ++++++-- app/Model/Entity/Desc.php | 11 +++++-- app/Model/Entity/User.php | 30 +++++++++++-------- app/Model/Entity/User3.php | 13 +++++--- app/Model/Logic/ApolloLogic.php | 11 +++++-- app/Model/Logic/BreakerLogic.php | 11 +++++-- app/Model/Logic/ConsulLogic.php | 12 ++++++-- app/Model/Logic/LimiterLogic.php | 11 +++++-- app/Model/Logic/MonitorLogic.php | 11 +++++-- app/Model/Logic/RequestBean.php | 14 +++++++-- app/Model/Logic/RequestBeanTwo.php | 11 +++++-- app/Model/Logic/UserLogic.php | 13 +++++--- app/Process/MonitorProcess.php | 10 +++++-- app/Process/Worker1Process.php | 10 +++++-- app/Process/Worker2Process.php | 10 +++++-- app/Rpc/Lib/UserInterface.php | 11 +++++-- app/Rpc/Middleware/ServiceMiddleware.php | 12 ++++++-- app/Rpc/Service/UserService.php | 12 ++++++-- app/Rpc/Service/UserServiceV2.php | 12 ++++++-- app/Task/Listener/FinishListener.php | 11 +++++-- app/Task/Task/SyncTask.php | 11 +++++-- app/Task/Task/TestTask.php | 11 +++++-- app/Tcp/Controller/DemoController.php | 8 +++++ app/Validator/CustomerValidator.php | 8 +++++ app/Validator/Rule/AlphaDashRule.php | 8 +++++ app/Validator/TestValidator.php | 8 +++++ app/WebSocket/Chat/HomeController.php | 8 +++++ app/WebSocket/ChatModule.php | 8 +++++ app/WebSocket/EchoModule.php | 8 +++++ app/WebSocket/Test/TestController.php | 8 +++++ app/WebSocket/TestModule.php | 8 +++++ app/bean.php | 9 +++++- bin/bootstrap.php | 11 +++++-- test/bootstrap.php | 9 +++++- 87 files changed, 769 insertions(+), 181 deletions(-) diff --git a/app/Annotation/Mapping/AlphaDash.php b/app/Annotation/Mapping/AlphaDash.php index 67bb235b..45aeeab4 100644 --- a/app/Annotation/Mapping/AlphaDash.php +++ b/app/Annotation/Mapping/AlphaDash.php @@ -1,4 +1,12 @@ restart(); } } -} \ No newline at end of file +} diff --git a/app/Console/Command/DemoCommand.php b/app/Console/Command/DemoCommand.php index 683c505c..4bf9b0fc 100644 --- a/app/Console/Command/DemoCommand.php +++ b/app/Console/Command/DemoCommand.php @@ -1,4 +1,12 @@ save(); - Log::profileStart("name"); + Log::profileStart('name'); $id = $user->getId(); $user = User::find($id)->toArray(); - Log::profileEnd("name"); + Log::profileEnd('name'); - Log::info("info message", ['a' => 'b']); + Log::info('info message', ['a' => 'b']); - CLog::info("second task run: %s ", date('Y-m-d H:i:s', time())); + CLog::info('second task run: %s ', date('Y-m-d H:i:s', time())); CLog::info(JsonHelper::encode($user)); } @@ -49,7 +57,6 @@ public function secondTask() */ public function minuteTask() { - CLog::info("minute task run: %s ", date('Y-m-d H:i:s', time())); + CLog::info('minute task run: %s ', date('Y-m-d H:i:s', time())); } - -} \ No newline at end of file +} diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php index bae2e370..e995d7b9 100644 --- a/app/Exception/ApiException.php +++ b/app/Exception/ApiException.php @@ -1,4 +1,12 @@ withStatus(500)->withContent(sprintf( - '%s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine() + '%s At %s line %d', + $e->getMessage(), + $e->getFile(), + $e->getLine() )); } diff --git a/app/Exception/Handler/WsMessageExceptionHandler.php b/app/Exception/Handler/WsMessageExceptionHandler.php index 1b533f6c..df3abd1c 100644 --- a/app/Exception/Handler/WsMessageExceptionHandler.php +++ b/app/Exception/Handler/WsMessageExceptionHandler.php @@ -1,4 +1,12 @@ getData(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/BreakerController.php b/app/Http/Controller/BreakerController.php index 96373323..59c141e3 100644 --- a/app/Http/Controller/BreakerController.php +++ b/app/Http/Controller/BreakerController.php @@ -1,5 +1,12 @@ logic->unFallback(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/CoController.php b/app/Http/Controller/CoController.php index f78912cc..c3328435 100644 --- a/app/Http/Controller/CoController.php +++ b/app/Http/Controller/CoController.php @@ -1,5 +1,12 @@ getId()]; } -} \ No newline at end of file +} diff --git a/app/Http/Controller/DbBuilderController.php b/app/Http/Controller/DbBuilderController.php index d0ec77bc..1fb59bd5 100644 --- a/app/Http/Controller/DbBuilderController.php +++ b/app/Http/Controller/DbBuilderController.php @@ -1,5 +1,12 @@ getId(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/ExceptionController.php b/app/Http/Controller/ExceptionController.php index 0fe8f281..2599ee61 100644 --- a/app/Http/Controller/ExceptionController.php +++ b/app/Http/Controller/ExceptionController.php @@ -1,4 +1,12 @@ toArray(); - User::db("test_error"); - User::db("test_error")->find($id); + User::db('test_error'); + User::db('test_error')->find($id); }); - User::db("test_error"); - User::db("test_error")->find($id); + User::db('test_error'); + User::db('test_error')->find($id); return $user; } @@ -237,7 +244,7 @@ public function insertId2(): bool public function desc(): array { $desc = new Desc(); - $desc->setDesc("desc"); + $desc->setDesc('desc'); $desc->save(); return Desc::find($desc->getId())->toArray(); @@ -257,4 +264,4 @@ public function getId(): int return $user->getId(); } -} \ No newline at end of file +} diff --git a/app/Http/Controller/TaskController.php b/app/Http/Controller/TaskController.php index 0168e143..241f8b64 100644 --- a/app/Http/Controller/TaskController.php +++ b/app/Http/Controller/TaskController.php @@ -1,5 +1,12 @@ getId(); Redis::set("$id", $user->toArray()); - Log::info("用户ID=" . $id . " timerId=" . $timerId); + Log::info('用户ID=' . $id . ' timerId=' . $timerId); sgo(function () use ($id) { $user = User::find($id)->toArray(); Log::info(JsonHelper::encode($user)); @@ -66,7 +73,7 @@ public function tick(): array $id = $user->getId(); Redis::set("$id", $user->toArray()); - Log::info("用户ID=" . $id . " timerId=" . $timerId); + Log::info('用户ID=' . $id . ' timerId=' . $timerId); sgo(function () use ($id) { $user = User::find($id)->toArray(); Log::info(JsonHelper::encode($user)); @@ -76,4 +83,4 @@ public function tick(): array return ['tick']; } -} \ No newline at end of file +} diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php index 213e3143..a0f6a95f 100644 --- a/app/Http/Controller/ValidatorController.php +++ b/app/Http/Controller/ValidatorController.php @@ -1,4 +1,12 @@ getParsedBody(); } @@ -39,7 +47,7 @@ function validateAll(Request $request): array * * @return array */ - function validateType(Request $request): array + public function validateType(Request $request): array { return $request->getParsedBody(); } @@ -54,7 +62,7 @@ function validateType(Request $request): array * * @return array */ - function validatePassword(Request $request): array + public function validatePassword(Request $request): array { return $request->getParsedBody(); } @@ -69,7 +77,7 @@ function validatePassword(Request $request): array * * @return array */ - function validateCustomer(Request $request): array + public function validateCustomer(Request $request): array { return $request->getParsedBody(); } diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php index 56797e4c..99f12436 100644 --- a/app/Http/Controller/ViewController.php +++ b/app/Http/Controller/ViewController.php @@ -1,5 +1,12 @@ agent->deregisterService('swoft'); } -} \ No newline at end of file +} diff --git a/app/Listener/ModelSavedListener.php b/app/Listener/ModelSavedListener.php index d9cc9be0..191ddb35 100644 --- a/app/Listener/ModelSavedListener.php +++ b/app/Listener/ModelSavedListener.php @@ -1,5 +1,12 @@ agent->registerService($service); // CLog::info('Swoft http register service success by consul!'); - } -} \ No newline at end of file +} diff --git a/app/Listener/Test/ShutDownListener.php b/app/Listener/Test/ShutDownListener.php index 56cd6f4b..4853cc3f 100644 --- a/app/Listener/Test/ShutDownListener.php +++ b/app/Listener/Test/ShutDownListener.php @@ -1,9 +1,15 @@ updateTime; } - } diff --git a/app/Model/Entity/Count2.php b/app/Model/Entity/Count2.php index 71f4ed65..1ab210e6 100644 --- a/app/Model/Entity/Count2.php +++ b/app/Model/Entity/Count2.php @@ -1,9 +1,15 @@ attributes = $attributes; } -} \ No newline at end of file +} diff --git a/app/Model/Entity/Desc.php b/app/Model/Entity/Desc.php index b9e0254d..df3e1a48 100644 --- a/app/Model/Entity/Desc.php +++ b/app/Model/Entity/Desc.php @@ -1,5 +1,12 @@ desc = $desc; } -} \ No newline at end of file +} diff --git a/app/Model/Entity/User.php b/app/Model/Entity/User.php index abc07444..67e40020 100644 --- a/app/Model/Entity/User.php +++ b/app/Model/Entity/User.php @@ -1,5 +1,12 @@ testJson; } - } diff --git a/app/Model/Entity/User3.php b/app/Model/Entity/User3.php index f310c2c0..07195bbd 100644 --- a/app/Model/Entity/User3.php +++ b/app/Model/Entity/User3.php @@ -1,15 +1,20 @@ userDesc = $userDesc; } -} \ No newline at end of file +} diff --git a/app/Model/Logic/ApolloLogic.php b/app/Model/Logic/ApolloLogic.php index 1316b622..1e6421b5 100644 --- a/app/Model/Logic/ApolloLogic.php +++ b/app/Model/Logic/ApolloLogic.php @@ -1,5 +1,12 @@ kv->get('/test/my/key'); var_dump($response->getBody(), $response->getResult()); } -} \ No newline at end of file +} diff --git a/app/Model/Logic/LimiterLogic.php b/app/Model/Logic/LimiterLogic.php index 90cec5fd..57c79bd6 100644 --- a/app/Model/Logic/LimiterLogic.php +++ b/app/Model/Logic/LimiterLogic.php @@ -1,5 +1,12 @@ handle($request); } -} \ No newline at end of file +} diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index cd30e12c..10123836 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -1,9 +1,15 @@ getTaskUniqid()); } -} \ No newline at end of file +} diff --git a/app/Task/Task/SyncTask.php b/app/Task/Task/SyncTask.php index 3c68b5ec..8f3441b9 100644 --- a/app/Task/Task/SyncTask.php +++ b/app/Task/Task/SyncTask.php @@ -1,5 +1,12 @@ Date: Fri, 13 Sep 2019 23:49:38 +0800 Subject: [PATCH 529/643] optimization demo database --- app/Listener/RanListener.php | 41 +---------- app/Migration/AddUser.php | 58 --------------- app/Migration/{AddMsg.php => User.php} | 7 +- app/Model/Entity/User.php | 72 +++---------------- app/bean.php | 2 +- composer.json | 3 +- database/AutoLoader.php | 43 +++++++++++ database/Migration/Count.php | 53 ++++++++++++++ .../Migration/Desc.php | 26 ++++--- 9 files changed, 125 insertions(+), 180 deletions(-) delete mode 100644 app/Migration/AddUser.php rename app/Migration/{AddMsg.php => User.php} (88%) create mode 100644 database/AutoLoader.php create mode 100644 database/Migration/Count.php rename app/Migration/Message.php => database/Migration/Desc.php (62%) diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index c1c5bcea..a9f77fd0 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -34,45 +34,8 @@ public function handle(EventInterface $event): void $querySql = $event->getParam(0); $bindings = $event->getParam(1); - $rawSql = $this->getRawSql($querySql, $bindings, $connection); + $rawSql = $connection->getRawSql($querySql, $bindings); -// output()->info($rawSql); - } - - /** - * Returns the raw SQL by inserting parameter values into the corresponding placeholders in [[sql]]. - * Note that the return value of this method should mainly be used for logging purpose. - * It is likely that this method returns an invalid SQL due to improper replacement of parameter placeholders. - * - * @param string $sql - * @param array $bindings - * @param Connection $connection - * - * @return string the raw SQL with parameter values inserted into the corresponding placeholders in [[sql]]. - */ - public function getRawSql(string $sql, array $bindings, Connection $connection) - { - if (empty($bindings)) { - return $sql; - } - foreach ($bindings as $name => $value) { - if (is_int($name)) { - $name = '?'; - } - - if (is_string($value) || is_array($value)) { - $param = $connection->getQueryGrammar()->quoteString($value); - } elseif (is_bool($value)) { - $param = ($value ? 'TRUE' : 'FALSE'); - } elseif ($value === null) { - $param = 'NULL'; - } else { - $param = (string)$value; - } - - $sql = StringHelper::replaceFirst($name, $param, $sql); - } - - return $sql; + // output()->info($rawSql); } } diff --git a/app/Migration/AddUser.php b/app/Migration/AddUser.php deleted file mode 100644 index acba4585..00000000 --- a/app/Migration/AddUser.php +++ /dev/null @@ -1,58 +0,0 @@ -increments('id'); - $blueprint->smallInteger('age'); - $blueprint->string('label', 10); - $blueprint->integer('balance'); - }); - - Schema::getSchemaBuilder('db.pool')->table('user', function (Blueprint $blueprint) { - $blueprint->comment('base user tables'); - }); - } - - /** - * @return void - * - * @throws ReflectionException - * @throws ContainerException - * @throws DbException - */ - public function down(): void - { - Schema::dropIfExists('users'); - - Schema::getSchemaBuilder('db.pool')->dropIfExists('users'); - } -} diff --git a/app/Migration/AddMsg.php b/app/Migration/User.php similarity index 88% rename from app/Migration/AddMsg.php rename to app/Migration/User.php index 3fd8b10d..6fc64539 100644 --- a/app/Migration/AddMsg.php +++ b/app/Migration/User.php @@ -14,7 +14,7 @@ * * @Migration(20190630164222) */ -class AddMsg extends BaseMigration +class User extends BaseMigration { /** * @return void @@ -22,12 +22,13 @@ class AddMsg extends BaseMigration public function up(): void { $sql = <<execute($dropSql); } diff --git a/app/Model/Entity/User.php b/app/Model/Entity/User.php index abc07444..3ff2ffa9 100644 --- a/app/Model/Entity/User.php +++ b/app/Model/Entity/User.php @@ -10,7 +10,7 @@ /** - * + * * Class User * * @since 2.0 @@ -20,65 +20,45 @@ class User extends Model { /** - * * @Id() + * * @Column() + * * @var int|null */ private $id; /** - * - * * @Column() + * * @var string */ private $name; /** - * - * * @Column() + * * @var int */ private $age; /** - * - * * @Column(hidden=true) + * * @var string */ private $password; /** - * - * * @Column(name="user_desc", prop="userDesc") + * * @var string */ private $userDesc; /** - * - * - * @Column() - * @var int|null - */ - private $add; - - /** - * - * - * @Column() - * @var int|null - */ - private $hahh; - - /** - * - * * @Column(name="test_json", prop="testJson") + * * @var array|null */ private $testJson; @@ -134,26 +114,6 @@ public function setUserDesc(string $userDesc): void $this->userDesc = $userDesc; } - /** - * @param int|null $add - * - * @return void - */ - public function setAdd(?int $add): void - { - $this->add = $add; - } - - /** - * @param int|null $hahh - * - * @return void - */ - public function setHahh(?int $hahh): void - { - $this->hahh = $hahh; - } - /** * @param array|null $testJson * @@ -204,22 +164,6 @@ public function getUserDesc(): string return $this->userDesc; } - /** - * @return int|null - */ - public function getAdd(): ?int - { - return $this->add; - } - - /** - * @return int|null - */ - public function getHahh(): ?int - { - return $this->hahh; - } - /** * @return array|null */ diff --git a/app/bean.php b/app/bean.php index 924704c0..b6a8aa85 100644 --- a/app/bean.php +++ b/app/bean.php @@ -91,7 +91,7 @@ 'database' => bean('db3') ], 'migrationManager' => [ - 'migrationPath' => '@app/Migration', + 'migrationPath' => '@database/Migration', ], 'redis' => [ 'class' => RedisDb::class, diff --git a/composer.json b/composer.json index 7a1d5d46..095fe9ef 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ }, "autoload": { "psr-4": { - "App\\": "app/" + "App\\": "app/", + "Database\\": "database/" }, "files": [ "app/Helper/Functions.php" diff --git a/database/AutoLoader.php b/database/AutoLoader.php new file mode 100644 index 00000000..3ad86973 --- /dev/null +++ b/database/AutoLoader.php @@ -0,0 +1,43 @@ + __DIR__, + ]; + } + + /** + * @return array + */ + public function metadata(): array + { + return []; + } +} diff --git a/database/Migration/Count.php b/database/Migration/Count.php new file mode 100644 index 00000000..a43bfd3f --- /dev/null +++ b/database/Migration/Count.php @@ -0,0 +1,53 @@ +schema->createIfNotExists('count', function (Blueprint $blueprint) { + $blueprint->comment('user count comment ...'); + + $blueprint->increments('id')->comment('primary'); + $blueprint->integer('user_id')->default('0')->comment('user table primary'); + $blueprint->integer('create_time')->default('0')->comment('create time'); + $blueprint->timestamp('update_time')->comment('update timestamp'); + + $blueprint->index(['user_id', 'create_time']); + + $blueprint->engine = 'Innodb'; + $blueprint->charset = 'utf8mb4'; + }); + } + + /** + * @throws ReflectionException + * @throws ContainerException + * @throws DbException + */ + public function down(): void + { + $this->schema->dropIfExists('count'); + } +} diff --git a/app/Migration/Message.php b/database/Migration/Desc.php similarity index 62% rename from app/Migration/Message.php rename to database/Migration/Desc.php index c5b4bb05..b8415b1a 100644 --- a/app/Migration/Message.php +++ b/database/Migration/Desc.php @@ -1,7 +1,7 @@ schema->createIfNotExists('messages', function (Blueprint $blueprint) { + $this->schema->createIfNotExists('desc', function (Blueprint $blueprint) { + $blueprint->comment = 'user desc'; + $blueprint->increments('id'); - $blueprint->text('content'); - $blueprint->timestamps(); + $blueprint->string('desc', 30); }); } /** - * @return void - * - * @throws ReflectionException * @throws ContainerException * @throws DbException + * @throws ReflectionException */ public function down(): void { - $this->schema->dropIfExists('messages'); + + $this->schema->dropIfExists('desc'); } } From 283ee7663e000cd995b75876245f9749999d70e0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 14 Sep 2019 10:02:09 +0800 Subject: [PATCH 530/643] up: revert vendor/bin/phpstan analyze --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 955f5c1b..ebc8174c 100644 --- a/composer.json +++ b/composer.json @@ -58,6 +58,7 @@ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "test": [ + "./vendor/bin/phpstan analyze", "./vendor/bin/phpunit -c phpunit.xml" ], "cs-fix": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff" From dd32a74bf93204da87efb648e8b182c1c92116c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Sat, 14 Sep 2019 04:24:00 +0200 Subject: [PATCH 531/643] Follow changes in swoole-ide-helper /src -> /output --- phpstan.neon.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 4a69c118..1691c3ee 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -8,7 +8,7 @@ parameters: autoload_files: - %currentWorkingDirectory%/test/bootstrap.php autoload_directories: - - %currentWorkingDirectory%/vendor/swoft/swoole-ide-helper/src/namespace/ + - %currentWorkingDirectory%/vendor/swoft/swoole-ide-helper/output/namespace/ dynamicConstantNames: - APP_DEBUG ignoreErrors: From 914918fc2bbd5efd4cb0f4b91c13e50b13881dc5 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Sep 2019 10:43:42 +0800 Subject: [PATCH 532/643] fix phpstan error --- app/WebSocket/Test/TestController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index ad489a30..c6080d57 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -86,7 +86,7 @@ public function injectMessage(Message $msg): void /** * Message command is: 'echo' * - * @param $data + * @param string $data * @MessageMapping(root=true) */ public function echo($data): void @@ -117,7 +117,7 @@ public function hi(Request $req, Response $res): void /** * Message command is: 'bin' * - * @param $data + * @param string $data * @MessageMapping("bin", root=true) */ public function binary($data): void @@ -138,7 +138,7 @@ public function pong(): void /** * Message command is: 'test.ar' * - * @param $data + * @param string $data * @MessageMapping("ar") * * @return string From 7a905f1feb93d3ba04ff9a839842be31515ee4bb Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Sep 2019 11:03:41 +0800 Subject: [PATCH 533/643] Update phpstan.neon.dist --- phpstan.neon.dist | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 1691c3ee..d8ee581c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -14,6 +14,8 @@ parameters: ignoreErrors: # Variable type - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' + - '#^Call to an undefined method Swoft\\Session\\SessionInterface::push\(\)#' + - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' # These are ignored for now - path: %currentWorkingDirectory%/app/Exception/Handler/WsMessageExceptionHandler.php @@ -48,6 +50,3 @@ parameters: - path: %currentWorkingDirectory%/app/WebSocket/ChatModule.php message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' - - - path: %currentWorkingDirectory%/app/WebSocket/EchoModule.php - message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' From a182d9e140e1f7530ea917f42acd7cf335918772 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Sep 2019 11:24:47 +0800 Subject: [PATCH 534/643] fix phpstan analyze error --- phpstan.neon.dist | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index d8ee581c..a3981a6c 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -15,11 +15,11 @@ parameters: # Variable type - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' - '#^Call to an undefined method Swoft\\Session\\SessionInterface::push\(\)#' + - '#^Call to an undefined method Swoft\\Session\\SessionInterface::getFd\(\)#' - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' + - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' + - '#^Call to an undefined method Psr\Http\Message\ServerRequestInterface::getUriPath\(\)#' # These are ignored for now - - - path: %currentWorkingDirectory%/app/Exception/Handler/WsMessageExceptionHandler.php - message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' - path: %currentWorkingDirectory%/app/Http/Controller/DbModelController.php message: '#^Method App\\Http\\Controller\\DbModelController::getId\(\) should return int but returns int\|null\.$#' @@ -44,9 +44,4 @@ parameters: - path: %currentWorkingDirectory%/app/Validator/Rule/AlphaDashRule.php message: '#^Call to an undefined method object::getMessage\(\)\.$#' - - - path: %currentWorkingDirectory%/app/WebSocket/Chat/HomeController.php - message: '#^Call to an undefined method Swoft\\Session\\SessionInterface::push\(\)\.$#' - - - path: %currentWorkingDirectory%/app/WebSocket/ChatModule.php - message: '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' + From 0ac96207ed2f9ead28b308fb7b2e20ec9014d95e Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 15 Sep 2019 12:04:02 +0800 Subject: [PATCH 535/643] Update phpstan.neon.dist --- phpstan.neon.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a3981a6c..a0b60b58 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -18,7 +18,7 @@ parameters: - '#^Call to an undefined method Swoft\\Session\\SessionInterface::getFd\(\)#' - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' - - '#^Call to an undefined method Psr\Http\Message\ServerRequestInterface::getUriPath\(\)#' + - '#^Call to an undefined method\sPsr\\Http\\Message\\ServerRequestInterface::getUriPath\(\)#' # These are ignored for now - path: %currentWorkingDirectory%/app/Http/Controller/DbModelController.php From ca25c71263993be34bfb44ada5e54c91522ef2cb Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 17 Sep 2019 17:25:12 +0800 Subject: [PATCH 536/643] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..dd84ea78 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From 321991de5657b5555f04bd31d9831bb3bff2b41a Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 17 Sep 2019 19:01:11 +0800 Subject: [PATCH 537/643] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..689db0a8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,32 +7,34 @@ assignees: '' --- +| Q | A +| ------------------- | ----- +| Bug report? | yes/no +| Feature request? | yes/no +| Swoft version | x.y.z +| Swoole version | x.y.z (by `php --ri swoole`) +| PHP version | x.y.z (by `php -v`) +| Runtime environment | Win10/Mac/CentOS 7/Ubuntu/Docker etc. + **Describe the bug** A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] +**Details** + +> Describe what you are trying to achieve and what goes wrong. + +```php +// paste output here +``` -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +> Provide minimal script to reproduce the issue -**Additional context** -Add any other context about the problem here. +```php +// paste code +``` From afa80504b5e4c28f24da419d4b5bfea2bfb7bd80 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 17 Sep 2019 19:01:34 +0800 Subject: [PATCH 538/643] Delete ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index be97a863..00000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ -| Q | A -| ------------------- | ----- -| Bug report? | yes/no -| Feature request? | yes/no -| Swoft version | x.y.z -| Swoole version | x.y.z (by `php --ri swoole`) -| PHP version | x.y.z (by `php -v`) -| Runtime environment | Win10/Mac/CentOS 7/Ubuntu/Docker etc. - -**Details** - -> Describe what you are trying to achieve and what goes wrong. - -```php -// paste output here -``` - -> Provide minimal script to reproduce the issue - -```php -// paste code -``` - From a558f1e0d2c4bafc7d5d26e12cf3dce7aef174f4 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 17 Sep 2019 21:16:34 +0800 Subject: [PATCH 539/643] Create CONTRIBUTING.md --- CONTRIBUTING.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..b2b3990b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# CONTRIBUTING + +## Contributing code via Github + +Swoft currently uses Git to control the version of the program. If you want to contribute source code to Swoft, please get an overview of how Git is used. We currently host the project on GitHub, and any GitHub user can contribute code to us. + +The way to participate is very simple, fork a swoft-component or swoft-ext code into your warehouse, modify and submit, and send us a pull request, we will promptly review the code and process your application. After the review, your code will be merged into our repository, so you will automatically appear on the contributor list, very convenient. + +We hope that the code you contributed will be: + +- Swoft's coding specification +- Appropriate comments that others can read +- Follow the Apache2 open source protocol +- Submit a commit message must be in English + +> If you would like more details or have any questions, please continue reading below + +Precautions + +- PSR-2 is selected for the code formatting standard of this project; +- class name and class file name follow PSR-4; +- For the processing of Issues, use the commit title such as `fix swoft-cloud/swoft#xxx(Issue ID)` to directly close the issue. +- The system will automatically test and modify on PHP 7.1+ (`7.1 7.2 7.3`) Swoole 4.4.1+ +- The administrator will not merge the changes that caused CI faild. If CI faild appears, please check your source code or modify the corresponding unit test file. + +## GitHub Issue + +GitHub provides the Issue feature, which can be used to: + +- Raise a bug +- propose functional improvements +- Feedback experience + +This feature should not be used for: + +- Unfriendly remarks + +## Quick modification + +GitHub provides the ability to quickly edit files + +- Log in to your GitHub account; +- Browse the project file and find the file to be modified; +- Click the pencil icon in the upper right corner to modify it; +- Fill in Commit changes related content (Title is required) +- Submit changes, wait for CI verification and administrator merge. + +> This method is suitable for modifying the document project. If you need to submit a large number of changes at once, please continue reading the following + +## Complete process + +- fork swoft-component or swoft-ext project; +- Clone your fork project to the local; +- Create a new branch and checkout the new branch; +- Make changes. If your changes include additions or subtractions of methods or functions, please remember to modify the unit test file. +- Push your local repository to GitHub; +- submit a pull request; +- Wait for CI verification (if you don't pass, you need to fix the code yourself, GitHub will automatically update your pull request); +- Waiting for administrator processing + +## Precautions + +If you have any questions about the above process, please check out the GIT tutorial; + +For different aspects of the code, create different branches in your own fork project. From 98c69d69b1a0e1458c662ac12457fb9126246774 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 20 Sep 2019 13:27:05 +0800 Subject: [PATCH 540/643] fix: phpcs and phpstan error --- .php_cs | 2 +- app/Http/Controller/ExceptionController.php | 1 - app/Listener/DeregisterServiceListener.php | 6 +----- app/WebSocket/Test/TestController.php | 6 ++++-- phpstan.neon.dist | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.php_cs b/.php_cs index 15759a97..9c552f9e 100644 --- a/.php_cs +++ b/.php_cs @@ -29,7 +29,7 @@ return PhpCsFixer\Config::create() ->setFinder( PhpCsFixer\Finder::create() ->exclude('public') - ->exclude('resources') + ->exclude('resource') ->exclude('config') ->exclude('runtime') ->exclude('vendor') diff --git a/app/Http/Controller/ExceptionController.php b/app/Http/Controller/ExceptionController.php index 812d891b..8a0c1a6e 100644 --- a/app/Http/Controller/ExceptionController.php +++ b/app/Http/Controller/ExceptionController.php @@ -17,7 +17,6 @@ use Throwable; use function trigger_error; use const E_USER_ERROR; -use const E_USER_NOTICE; /** * @Controller(prefix="ex") diff --git a/app/Listener/DeregisterServiceListener.php b/app/Listener/DeregisterServiceListener.php index 4b3c5c80..c863c456 100644 --- a/app/Listener/DeregisterServiceListener.php +++ b/app/Listener/DeregisterServiceListener.php @@ -10,12 +10,8 @@ namespace App\Listener; -use ReflectionException; use Swoft\Bean\Annotation\Mapping\Inject; -use Swoft\Bean\Exception\ContainerException; use Swoft\Consul\Agent; -use Swoft\Consul\Exception\ClientException; -use Swoft\Consul\Exception\ServerException; use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; @@ -46,6 +42,6 @@ public function handle(EventInterface $event): void /** @var HttpServer $httpServer */ $httpServer = $event->getTarget(); - $this->agent->deregisterService('swoft'); + $this->agent->deregisterService('swoft'); } } diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index 8235e476..0b7ab9a0 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -13,6 +13,7 @@ use Swoft\Session\Session; use Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping; use Swoft\WebSocket\Server\Annotation\Mapping\WsController; +use Swoft\WebSocket\Server\Connection; use Swoft\WebSocket\Server\Message\Message; use Swoft\WebSocket\Server\Message\Request; use Swoft\WebSocket\Server\Message\Response; @@ -48,6 +49,7 @@ public function index(): void public function close(Message $msg): void { $data = $msg->getData(); + /** @var Connection $conn */ $conn = Session::mustGet(); $fd = is_numeric($data) ? (int)$data : $conn->getFd(); @@ -120,8 +122,8 @@ public function hi(Request $req, Response $res): void /** * Message command is: 'bin' * - * @param $data * @MessageMapping("bin", root=true, opcode=2) + * @param string $data * * @return string */ @@ -144,8 +146,8 @@ public function pong(): void /** * Message command is: 'test.ar' * - * @param string $data * @MessageMapping("ar") + * @param string $data * * @return string */ diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a0b60b58..249e6daa 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -17,7 +17,7 @@ parameters: - '#^Call to an undefined method Swoft\\Session\\SessionInterface::push\(\)#' - '#^Call to an undefined method Swoft\\Session\\SessionInterface::getFd\(\)#' - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' - - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' + # - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' - '#^Call to an undefined method\sPsr\\Http\\Message\\ServerRequestInterface::getUriPath\(\)#' # These are ignored for now - From a5175ad30ca98b99eb7b9a1dfd8b10b878c9c199 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Fri, 20 Sep 2019 14:14:13 +0800 Subject: [PATCH 541/643] add database unit --- .github/ISSUE_TEMPLATE/bug_report.md | 40 ++++++++++++++ CONTRIBUTING.md | 65 +++++++++++++++++++++++ app/Console/Command/TestCommand.php | 4 +- app/Http/Controller/DbModelController.php | 51 +++++++++++++++++- app/Migration/User.php | 2 +- 5 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..689db0a8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +| Q | A +| ------------------- | ----- +| Bug report? | yes/no +| Feature request? | yes/no +| Swoft version | x.y.z +| Swoole version | x.y.z (by `php --ri swoole`) +| PHP version | x.y.z (by `php -v`) +| Runtime environment | Win10/Mac/CentOS 7/Ubuntu/Docker etc. + +**Describe the bug** +A clear and concise description of what the bug is. + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Details** + +> Describe what you are trying to achieve and what goes wrong. + +```php +// paste output here +``` + +> Provide minimal script to reproduce the issue + +```php +// paste code +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..b2b3990b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# CONTRIBUTING + +## Contributing code via Github + +Swoft currently uses Git to control the version of the program. If you want to contribute source code to Swoft, please get an overview of how Git is used. We currently host the project on GitHub, and any GitHub user can contribute code to us. + +The way to participate is very simple, fork a swoft-component or swoft-ext code into your warehouse, modify and submit, and send us a pull request, we will promptly review the code and process your application. After the review, your code will be merged into our repository, so you will automatically appear on the contributor list, very convenient. + +We hope that the code you contributed will be: + +- Swoft's coding specification +- Appropriate comments that others can read +- Follow the Apache2 open source protocol +- Submit a commit message must be in English + +> If you would like more details or have any questions, please continue reading below + +Precautions + +- PSR-2 is selected for the code formatting standard of this project; +- class name and class file name follow PSR-4; +- For the processing of Issues, use the commit title such as `fix swoft-cloud/swoft#xxx(Issue ID)` to directly close the issue. +- The system will automatically test and modify on PHP 7.1+ (`7.1 7.2 7.3`) Swoole 4.4.1+ +- The administrator will not merge the changes that caused CI faild. If CI faild appears, please check your source code or modify the corresponding unit test file. + +## GitHub Issue + +GitHub provides the Issue feature, which can be used to: + +- Raise a bug +- propose functional improvements +- Feedback experience + +This feature should not be used for: + +- Unfriendly remarks + +## Quick modification + +GitHub provides the ability to quickly edit files + +- Log in to your GitHub account; +- Browse the project file and find the file to be modified; +- Click the pencil icon in the upper right corner to modify it; +- Fill in Commit changes related content (Title is required) +- Submit changes, wait for CI verification and administrator merge. + +> This method is suitable for modifying the document project. If you need to submit a large number of changes at once, please continue reading the following + +## Complete process + +- fork swoft-component or swoft-ext project; +- Clone your fork project to the local; +- Create a new branch and checkout the new branch; +- Make changes. If your changes include additions or subtractions of methods or functions, please remember to modify the unit test file. +- Push your local repository to GitHub; +- submit a pull request; +- Wait for CI verification (if you don't pass, you need to fix the code yourself, GitHub will automatically update your pull request); +- Waiting for administrator processing + +## Precautions + +If you have any questions about the above process, please check out the GIT tutorial; + +For different aspects of the code, create different branches in your own fork project. diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index d6d24732..77f8a392 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -91,6 +91,8 @@ private function uris(): array '/dbModel/delete', '/dbModel/save', '/dbModel/batchUpdate', + '/dbModel/batchUpdateOrInsert', + '/dbModel/propWhere', '/selectDb/modelNotExistDb', '/selectDb/queryNotExistDb', '/selectDb/dbNotExistDb', @@ -98,7 +100,7 @@ private function uris(): array '/selectDb/queryDb', '/selectDb/dbDb', '/selectDb/select', - '/builder/schema' + '/builder/schema', ], 'task' => [ '/task/getListByCo', diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 777a881f..c25e291d 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -77,7 +77,7 @@ public function update(): array { $id = $this->getId(); - User::updateOrInsert(['id' => $id], ['name' => 'swoft']); + User::updateOrInsert(['id' => $id], ['name' => 'swoft', 'userDesc' => 'swoft']); $user = User::find($id); @@ -120,7 +120,7 @@ public function getId(): int * @return array * @throws Throwable */ - public function batchUpdate() + public function batchUpdate(): array { // User::truncate(); User::updateOrCreate(['id' => 1], ['age' => 23]); @@ -148,4 +148,51 @@ public function batchUpdate() return $updateResults; } + + /** + * @RequestMapping() + * + * @return array + * @throws Throwable + */ + public function propWhere(): array + { + $id = $this->getId(); + + User::updateOrInsert(['id' => $id], ['userDesc' => 'swoft']); + + $user = User::whereProp(['userDesc' => 'swoft'])->first(); + + return $user->toArray(); + } + + /** + * @RequestMapping() + * + * @return bool + * @throws Throwable + */ + public function batchUpdateOrInsert(): bool + { + $values = [ + [ + 'age' => 18, + 'user_desc' => 'swoft' . random_int(1, 2), + 'test_json' => [] + ], + [ + 'age' => 19, + 'user_desc' => 'swoft1' . random_int(2, 3), + 'test_json' => null + ], + [ + 'age' => 20, + 'user_desc' => 'swoft2' . random_int(4, 6), + 'test_json' => ['test' => 1] + ], + ]; + $baseWhere = ['age' => [18, 19, 20]]; + + return User::batchUpdateOrInsert($values, $baseWhere, ['user_desc'], ['user_desc']); + } } diff --git a/app/Migration/User.php b/app/Migration/User.php index c01ea114..2292766d 100644 --- a/app/Migration/User.php +++ b/app/Migration/User.php @@ -28,7 +28,7 @@ class User extends BaseMigration public function up(): void { $sql = << Date: Fri, 20 Sep 2019 23:05:25 +0800 Subject: [PATCH 542/643] fix dbmodel demo --- app/Http/Controller/DbModelController.php | 2 +- app/Listener/RanListener.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index c25e291d..cba16791 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -191,7 +191,7 @@ public function batchUpdateOrInsert(): bool 'test_json' => ['test' => 1] ], ]; - $baseWhere = ['age' => [18, 19, 20]]; + $baseWhere = ['age' => 18]; return User::batchUpdateOrInsert($values, $baseWhere, ['user_desc'], ['user_desc']); } diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index d9bd9549..a0e7c4fe 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -42,6 +42,6 @@ public function handle(EventInterface $event): void $rawSql = $connection->getRawSql($querySql, $bindings); - // output()->info($rawSql); + //output()->info($rawSql); } } From 47730666baaae79b2b1d150a57c69741a2457a80 Mon Sep 17 00:00:00 2001 From: stelin <794774870@qq.com> Date: Fri, 20 Sep 2019 23:11:05 +0800 Subject: [PATCH 543/643] modify bean.php --- app/bean.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/bean.php b/app/bean.php index 924704c0..e3a0c768 100644 --- a/app/bean.php +++ b/app/bean.php @@ -25,8 +25,8 @@ 'logFile' => '@runtime/logs/error-%d{Y-m-d}.log', ], 'logger' => [ - 'flushRequest' => true, - 'enable' => true, + 'flushRequest' => false, + 'enable' => false, 'json' => false, ], 'httpServer' => [ From ce4c73ce9537dd8126020e9377867147dea4e650 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sat, 21 Sep 2019 01:00:34 +0800 Subject: [PATCH 544/643] close --- app/Listener/DeregisterServiceListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Listener/DeregisterServiceListener.php b/app/Listener/DeregisterServiceListener.php index c863c456..7fbc1340 100644 --- a/app/Listener/DeregisterServiceListener.php +++ b/app/Listener/DeregisterServiceListener.php @@ -42,6 +42,6 @@ public function handle(EventInterface $event): void /** @var HttpServer $httpServer */ $httpServer = $event->getTarget(); - $this->agent->deregisterService('swoft'); + //$this->agent->deregisterService('swoft'); } } From 97c579f7f8cb8fe184472091d51c04ad05fa1959 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sat, 21 Sep 2019 01:08:36 +0800 Subject: [PATCH 545/643] fix --- app/Http/Controller/DbModelController.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 9d022219..de9a02aa 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -158,9 +158,7 @@ public function batchUpdate(): array */ public function propWhere(): array { - $id = $this->getId(); - - User::updateOrInsert(['id' => $id], ['userDesc' => 'swoft']); + User::updateOrInsert(['id' => 1000], ['userDesc' => 'swoft']); $user = User::whereProp(['userDesc' => 'swoft'])->first(); From 072a00bb1a07911bc1689458b4445624300ce49d Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sat, 21 Sep 2019 01:22:22 +0800 Subject: [PATCH 546/643] remove batch update --- app/Console/Command/TestCommand.php | 1 - app/Http/Controller/DbModelController.php | 30 ----------------------- 2 files changed, 31 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 77f8a392..7ad6b094 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -91,7 +91,6 @@ private function uris(): array '/dbModel/delete', '/dbModel/save', '/dbModel/batchUpdate', - '/dbModel/batchUpdateOrInsert', '/dbModel/propWhere', '/selectDb/modelNotExistDb', '/selectDb/queryNotExistDb', diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index de9a02aa..1eca700b 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -164,34 +164,4 @@ public function propWhere(): array return $user->toArray(); } - - /** - * @RequestMapping() - * - * @return bool - * @throws Throwable - */ - public function batchUpdateOrInsert(): bool - { - $values = [ - [ - 'age' => 18, - 'user_desc' => 'swoft' . random_int(1, 2), - 'test_json' => [] - ], - [ - 'age' => 19, - 'user_desc' => 'swoft1' . random_int(2, 3), - 'test_json' => null - ], - [ - 'age' => 20, - 'user_desc' => 'swoft2' . random_int(4, 6), - 'test_json' => ['test' => 1] - ], - ]; - $baseWhere = ['age' => 18]; - - return User::batchUpdateOrInsert($values, $baseWhere, ['user_desc'], ['user_desc']); - } } From 46f9ad61c609997089517e56563a4f02a6af7e70 Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 23 Sep 2019 11:45:49 +0800 Subject: [PATCH 547/643] up: remove strict_types=1 from bin/swoft --- bin/swoft | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/swoft b/bin/swoft index d50ed832..393af860 100644 --- a/bin/swoft +++ b/bin/swoft @@ -1,5 +1,5 @@ #!/usr/bin/env php - Date: Mon, 23 Sep 2019 17:41:33 +0800 Subject: [PATCH 548/643] update demo class --- app/WebSocket/Chat/HomeController.php | 4 ++-- app/WebSocket/Test/TestController.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php index ee13c418..e4f3e3d5 100644 --- a/app/WebSocket/Chat/HomeController.php +++ b/app/WebSocket/Chat/HomeController.php @@ -38,7 +38,7 @@ public function index(): void * @param string $data * @MessageMapping() */ - public function echo($data): void + public function echo(string $data): void { Session::mustGet()->push('(home.echo)Recv: ' . $data); } @@ -51,7 +51,7 @@ public function echo($data): void * * @return string */ - public function autoReply($data): string + public function autoReply(string $data): string { return '(home.ar)Recv: ' . $data; } diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index 0b7ab9a0..960dc67b 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -94,7 +94,7 @@ public function injectMessage(Message $msg): void * @param string $data * @MessageMapping(root=true) */ - public function echo($data): void + public function echo(string $data): void { Session::mustGet()->push('(echo)Recv: ' . $data); } @@ -127,7 +127,7 @@ public function hi(Request $req, Response $res): void * * @return string */ - public function binary($data): string + public function binary(string $data): string { // Session::mustGet()->push('Binary: ' . $data, \WEBSOCKET_OPCODE_BINARY); return 'Binary: ' . $data; @@ -151,7 +151,7 @@ public function pong(): void * * @return string */ - public function autoReply($data): string + public function autoReply(string $data): string { return '(home.ar)Recv: ' . $data; } From 69f047eeb51082ea9e9f1234adb49ef0cdd1a8a5 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 23 Sep 2019 18:21:23 +0800 Subject: [PATCH 549/643] update dockerfile and mv crontab folder to app/Task --- Dockerfile | 2 +- app/{ => Task}/Crontab/CronTask.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/{ => Task}/Crontab/CronTask.php (97%) diff --git a/Dockerfile b/Dockerfile index 05ae0362..6c3dfb9b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.4.5 \ + SWOOLE_VERSION=4.4.6 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends diff --git a/app/Crontab/CronTask.php b/app/Task/Crontab/CronTask.php similarity index 97% rename from app/Crontab/CronTask.php rename to app/Task/Crontab/CronTask.php index 7af95957..4cc3f016 100644 --- a/app/Crontab/CronTask.php +++ b/app/Task/Crontab/CronTask.php @@ -8,7 +8,7 @@ * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ -namespace App\Crontab; +namespace App\Task\Crontab; use Exception; use Swoft\Crontab\Annotaion\Mapping\Cron; From 658afbf3866e2af0fd64daf16bc347a96e827419 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 23 Sep 2019 18:32:29 +0800 Subject: [PATCH 550/643] fix: php-cs-fixer check error --- app/Listener/RanListener.php | 1 - composer.json | 3 ++- database/AutoLoader.php | 1 - database/Migration/Desc.php | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/Listener/RanListener.php b/app/Listener/RanListener.php index d923528c..242054fd 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/RanListener.php @@ -15,7 +15,6 @@ use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; -use Swoft\Stdlib\Helper\StringHelper; /** * Class RanListener diff --git a/composer.json b/composer.json index 66d77123..d9c1185c 100644 --- a/composer.json +++ b/composer.json @@ -62,6 +62,7 @@ "./vendor/bin/phpstan analyze", "./vendor/bin/phpunit -c phpunit.xml" ], - "cs-fix": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff" + "cs-fix": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff", + "do-cs-fix": "./bin/php-cs-fixer fix" } } diff --git a/database/AutoLoader.php b/database/AutoLoader.php index fdfa1d21..bc84c55a 100644 --- a/database/AutoLoader.php +++ b/database/AutoLoader.php @@ -20,7 +20,6 @@ */ class AutoLoader extends SwoftComponent { - /** * Class constructor. */ diff --git a/database/Migration/Desc.php b/database/Migration/Desc.php index 322593e0..ee215ff0 100644 --- a/database/Migration/Desc.php +++ b/database/Migration/Desc.php @@ -48,7 +48,6 @@ public function up(): void */ public function down(): void { - $this->schema->dropIfExists('desc'); } } From af27458f6e3c72c6bd4abda9b2c2627bc3a9c28a Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 23 Sep 2019 18:36:15 +0800 Subject: [PATCH 551/643] fix: phpstan check error --- app/Http/Controller/DbModelController.php | 3 ++- phpstan.neon.dist | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Http/Controller/DbModelController.php b/app/Http/Controller/DbModelController.php index 1eca700b..f7e68a6c 100644 --- a/app/Http/Controller/DbModelController.php +++ b/app/Http/Controller/DbModelController.php @@ -160,8 +160,9 @@ public function propWhere(): array { User::updateOrInsert(['id' => 1000], ['userDesc' => 'swoft']); + /** @var User|null $user */ $user = User::whereProp(['userDesc' => 'swoft'])->first(); - return $user->toArray(); + return $user ? $user->toArray() : []; } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 249e6daa..8760f4a9 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -14,8 +14,8 @@ parameters: ignoreErrors: # Variable type - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' - - '#^Call to an undefined method Swoft\\Session\\SessionInterface::push\(\)#' - - '#^Call to an undefined method Swoft\\Session\\SessionInterface::getFd\(\)#' + - '#^Call to an undefined method Swoft\\Contract\\SessionInterface::push\(\)#' + # - '#^Call to an undefined method Swoft\\Session\\SessionInterface::getFd\(\)#' - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' # - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' - '#^Call to an undefined method\sPsr\\Http\\Message\\ServerRequestInterface::getUriPath\(\)#' From 1ae5022a6f03581883555ff230409163386e5371 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 23 Sep 2019 18:39:47 +0800 Subject: [PATCH 552/643] update .travis.yml --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4d7fae11..90be2efc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ install: - | echo "no" | pecl install -f redis - | - wget https://github.com/swoole/swoole-src/archive/v4.3.3.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole + wget https://github.com/swoole/swoole-src/archive/v4.3.6.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - | wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" @@ -26,3 +26,4 @@ before_script: script: - composer cs-fix - composer test + - php bin/swoft dinfo:env From 755b2e544b33c34d0e3a86c24e36d6363ff95869 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 23 Sep 2019 19:37:17 +0800 Subject: [PATCH 553/643] update swoole to 4.4.6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 90be2efc..4ee4ec8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ install: - | echo "no" | pecl install -f redis - | - wget https://github.com/swoole/swoole-src/archive/v4.3.6.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole + wget https://github.com/swoole/swoole-src/archive/v4.4.6.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - | wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" From c124de3eb3c1bb2ec9f51d2909b997e2aa2d1d30 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 23 Sep 2019 20:21:53 +0800 Subject: [PATCH 554/643] fix ci error --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4ee4ec8a..70ca9ccb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,4 +26,4 @@ before_script: script: - composer cs-fix - composer test - - php bin/swoft dinfo:env +# - php bin/swoft dinfo:env From bbeb91e7afe38770d382beffb3daa933300e5ad7 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 24 Sep 2019 13:40:26 +0800 Subject: [PATCH 555/643] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 79d27fc9..72bd2212 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,8 @@ Through three years of accumulation and direction exploration, Swoft has made Sw ## Discuss -- [swoft-cloud/community](https://gitter.im/swoft-cloud/community) +- Gitter.im https://gitter.im/swoft-cloud/community +- Reddit https://www.reddit.com/r/swoft/ - QQ Group1: 548173319 - QQ Group2: 778656850 From e6f3a422e4f67dc9f332876c7801ee1540ae2f71 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 25 Sep 2019 10:47:02 +0800 Subject: [PATCH 556/643] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72bd2212..ca1b0e57 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw ## Discuss +- Forum https://github.com/swoft-cloud/forum/issues - Gitter.im https://gitter.im/swoft-cloud/community - Reddit https://www.reddit.com/r/swoft/ - QQ Group1: 548173319 From 00e3fc2a07b9f8d5a54deeedab349713ed6fb8d3 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 25 Sep 2019 21:04:33 +0800 Subject: [PATCH 557/643] up: upgrade swoole to 4.4.7 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6c3dfb9b..d3e5f84c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.4.6 \ + SWOOLE_VERSION=4.4.7 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From 23753acaa220a4839fc219a2642368f2ad154993 Mon Sep 17 00:00:00 2001 From: zzhpeng Date: Thu, 26 Sep 2019 21:45:24 +0800 Subject: [PATCH 558/643] fix: rpcExceptionHandler just show error message rpcExceptionHandler just show error message when debug is false --- app/Exception/Handler/RpcExceptionHandler.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/Exception/Handler/RpcExceptionHandler.php b/app/Exception/Handler/RpcExceptionHandler.php index e03bc457..1c1dfd17 100644 --- a/app/Exception/Handler/RpcExceptionHandler.php +++ b/app/Exception/Handler/RpcExceptionHandler.php @@ -38,12 +38,14 @@ class RpcExceptionHandler extends RpcErrorHandler */ public function handle(Throwable $e, Response $response): Response { - // Debug is false + // Debug is false if (!APP_DEBUG) { + //just show error message + $error = Error::new($e->getCode(), $e->getMessage(), null); + } else { $message = sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()); $error = Error::new($e->getCode(), $message, null); - } else { - $error = Error::new($e->getCode(), $e->getMessage(), null); + } Debug::log('Rpc server error(%s)', $e->getMessage()); From dcc2dee1573adfd0926145e0bbd473266cb6d640 Mon Sep 17 00:00:00 2001 From: zzhpeng Date: Thu, 26 Sep 2019 21:57:04 +0800 Subject: [PATCH 559/643] update: trim and format --- app/Exception/Handler/RpcExceptionHandler.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/Exception/Handler/RpcExceptionHandler.php b/app/Exception/Handler/RpcExceptionHandler.php index 1c1dfd17..aed937eb 100644 --- a/app/Exception/Handler/RpcExceptionHandler.php +++ b/app/Exception/Handler/RpcExceptionHandler.php @@ -44,8 +44,7 @@ public function handle(Throwable $e, Response $response): Response $error = Error::new($e->getCode(), $e->getMessage(), null); } else { $message = sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()); - $error = Error::new($e->getCode(), $message, null); - + $error = Error::new($e->getCode(), $message, null); } Debug::log('Rpc server error(%s)', $e->getMessage()); From 4cceda998459e3f80dc9ee7da175021e59c893ec Mon Sep 17 00:00:00 2001 From: zzhpeng Date: Thu, 26 Sep 2019 22:21:27 +0800 Subject: [PATCH 560/643] update: PSR-2 format --- app/Exception/Handler/RpcExceptionHandler.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Exception/Handler/RpcExceptionHandler.php b/app/Exception/Handler/RpcExceptionHandler.php index aed937eb..a90c016f 100644 --- a/app/Exception/Handler/RpcExceptionHandler.php +++ b/app/Exception/Handler/RpcExceptionHandler.php @@ -38,15 +38,15 @@ class RpcExceptionHandler extends RpcErrorHandler */ public function handle(Throwable $e, Response $response): Response { - // Debug is false + // Debug is false if (!APP_DEBUG) { //just show error message $error = Error::new($e->getCode(), $e->getMessage(), null); } else { $message = sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()); - $error = Error::new($e->getCode(), $message, null); + $error = Error::new($e->getCode(), $message, null); } - + Debug::log('Rpc server error(%s)', $e->getMessage()); $response->setError($error); From 81d1dcf86353b4ee80cea3e7d81f195cd3d5ddb0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 26 Sep 2019 22:42:46 +0800 Subject: [PATCH 561/643] Update RpcExceptionHandler.php --- app/Exception/Handler/RpcExceptionHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Exception/Handler/RpcExceptionHandler.php b/app/Exception/Handler/RpcExceptionHandler.php index a90c016f..d9741d80 100644 --- a/app/Exception/Handler/RpcExceptionHandler.php +++ b/app/Exception/Handler/RpcExceptionHandler.php @@ -40,13 +40,13 @@ public function handle(Throwable $e, Response $response): Response { // Debug is false if (!APP_DEBUG) { - //just show error message + // just show error message $error = Error::new($e->getCode(), $e->getMessage(), null); } else { $message = sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()); $error = Error::new($e->getCode(), $message, null); } - + Debug::log('Rpc server error(%s)', $e->getMessage()); $response->setError($error); From 9eedd8d67a7528155a0a5d30dac1609600798734 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 16 Oct 2019 11:21:24 +0800 Subject: [PATCH 562/643] up: upgrade swoole to 4.4.8 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d3e5f84c..f36550e9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=4.3.0 \ - SWOOLE_VERSION=4.4.7 \ + SWOOLE_VERSION=4.4.8 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From dec2bc0cb70f89ab347daae7ecd5dde34cb1a2a8 Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 21 Oct 2019 17:54:09 +0800 Subject: [PATCH 563/643] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 689db0a8..1bafbce5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,7 +11,7 @@ assignees: '' | ------------------- | ----- | Bug report? | yes/no | Feature request? | yes/no -| Swoft version | x.y.z +| Swoft version | x.y.z (by `php bin/swoft -V`) | Swoole version | x.y.z (by `php --ri swoole`) | PHP version | x.y.z (by `php -v`) | Runtime environment | Win10/Mac/CentOS 7/Ubuntu/Docker etc. From 952a714fd417fa7bcb97897fab84090449432f86 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 24 Oct 2019 10:38:48 +0800 Subject: [PATCH 564/643] up: add tcp middleware examples, format some codes --- .../Handler/HttpExceptionHandler.php | 17 +++----- app/Exception/Handler/RpcExceptionHandler.php | 4 -- .../Handler/WsHandshakeExceptionHandler.php | 12 +----- .../Handler/WsMessageExceptionHandler.php | 5 --- app/Tcp/Controller/DemoController.php | 3 +- app/Tcp/Middleware/DemoMiddleware.php | 39 +++++++++++++++++++ app/Tcp/Middleware/GlobalTcpMiddleware.php | 39 +++++++++++++++++++ app/bean.php | 13 ++++++- 8 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 app/Tcp/Middleware/DemoMiddleware.php create mode 100644 app/Tcp/Middleware/GlobalTcpMiddleware.php diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index c9e347b2..4af35438 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -10,17 +10,15 @@ namespace App\Exception\Handler; -use const APP_DEBUG; -use function get_class; -use ReflectionException; -use function sprintf; -use Swoft\Bean\Exception\ContainerException; use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Http\Message\Response; use Swoft\Http\Server\Exception\Handler\AbstractHttpErrorHandler; use Swoft\Log\Helper\CLog; use Swoft\Log\Helper\Log; use Throwable; +use function get_class; +use function sprintf; +use const APP_DEBUG; /** * Class HttpExceptionHandler @@ -31,11 +29,9 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler { /** * @param Throwable $e - * @param Response $response + * @param Response $response * * @return Response - * @throws ReflectionException - * @throws ContainerException */ public function handle(Throwable $e, Response $response): Response { @@ -45,9 +41,8 @@ public function handle(Throwable $e, Response $response): Response // Debug is false if (!APP_DEBUG) { - return $response->withStatus(500)->withContent( - sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), $e->getLine()) - ); + return $response->withStatus(500)->withContent(sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), + $e->getLine())); } $data = [ diff --git a/app/Exception/Handler/RpcExceptionHandler.php b/app/Exception/Handler/RpcExceptionHandler.php index e03bc457..5b618f29 100644 --- a/app/Exception/Handler/RpcExceptionHandler.php +++ b/app/Exception/Handler/RpcExceptionHandler.php @@ -10,8 +10,6 @@ namespace App\Exception\Handler; -use ReflectionException; -use Swoft\Bean\Exception\ContainerException; use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Log\Debug; use Swoft\Rpc\Error; @@ -33,8 +31,6 @@ class RpcExceptionHandler extends RpcErrorHandler * @param Response $response * * @return Response - * @throws ReflectionException - * @throws ContainerException */ public function handle(Throwable $e, Response $response): Response { diff --git a/app/Exception/Handler/WsHandshakeExceptionHandler.php b/app/Exception/Handler/WsHandshakeExceptionHandler.php index 330bd465..4b560da7 100644 --- a/app/Exception/Handler/WsHandshakeExceptionHandler.php +++ b/app/Exception/Handler/WsHandshakeExceptionHandler.php @@ -10,8 +10,6 @@ namespace App\Exception\Handler; -use ReflectionException; -use Swoft\Bean\Exception\ContainerException; use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Http\Message\Response; use Swoft\WebSocket\Server\Exception\Handler\AbstractHandshakeErrorHandler; @@ -32,19 +30,13 @@ class WsHandshakeExceptionHandler extends AbstractHandshakeErrorHandler * @param Response $response * * @return Response - * @throws ReflectionException - * @throws ContainerException */ public function handle(Throwable $e, Response $response): Response { // Debug is false if (!APP_DEBUG) { - return $response->withStatus(500)->withContent(sprintf( - '%s At %s line %d', - $e->getMessage(), - $e->getFile(), - $e->getLine() - )); + return $response->withStatus(500)->withContent(sprintf('%s At %s line %d', $e->getMessage(), $e->getFile(), + $e->getLine())); } $data = [ diff --git a/app/Exception/Handler/WsMessageExceptionHandler.php b/app/Exception/Handler/WsMessageExceptionHandler.php index df3abd1c..a9f5d981 100644 --- a/app/Exception/Handler/WsMessageExceptionHandler.php +++ b/app/Exception/Handler/WsMessageExceptionHandler.php @@ -10,8 +10,6 @@ namespace App\Exception\Handler; -use ReflectionException; -use Swoft\Bean\Exception\ContainerException; use Swoft\Error\Annotation\Mapping\ExceptionHandler; use Swoft\Log\Helper\Log; use Swoft\WebSocket\Server\Exception\Handler\AbstractMessageErrorHandler; @@ -32,9 +30,6 @@ class WsMessageExceptionHandler extends AbstractMessageErrorHandler /** * @param Throwable $e * @param Frame $frame - * - * @throws ContainerException - * @throws ReflectionException */ public function handle(Throwable $e, Frame $frame): void { diff --git a/app/Tcp/Controller/DemoController.php b/app/Tcp/Controller/DemoController.php index 1f57c54c..d6216e4d 100644 --- a/app/Tcp/Controller/DemoController.php +++ b/app/Tcp/Controller/DemoController.php @@ -10,6 +10,7 @@ namespace App\Tcp\Controller; +use App\Tcp\Middleware\DemoMiddleware; use Swoft\Tcp\Server\Annotation\Mapping\TcpController; use Swoft\Tcp\Server\Annotation\Mapping\TcpMapping; use Swoft\Tcp\Server\Request; @@ -19,7 +20,7 @@ /** * Class DemoController * - * @TcpController() + * @TcpController(middlewares={DemoMiddleware::class}) */ class DemoController { diff --git a/app/Tcp/Middleware/DemoMiddleware.php b/app/Tcp/Middleware/DemoMiddleware.php new file mode 100644 index 00000000..d6526acf --- /dev/null +++ b/app/Tcp/Middleware/DemoMiddleware.php @@ -0,0 +1,39 @@ +before '; + + CLog::info('before handle'); + + $resp = $handler->handle($request); + + $resp->setData($start . $resp->getData() . ' after>'); + + CLog::info('after handle'); + + return $resp; + } +} diff --git a/app/Tcp/Middleware/GlobalTcpMiddleware.php b/app/Tcp/Middleware/GlobalTcpMiddleware.php new file mode 100644 index 00000000..b8fe6e0c --- /dev/null +++ b/app/Tcp/Middleware/GlobalTcpMiddleware.php @@ -0,0 +1,39 @@ +before '; + + CLog::info('before handle'); + + $resp = $handler->handle($request); + + $resp->setData($start . $resp->getData() . ' after>'); + + CLog::info('after handle'); + + return $resp; + } +} diff --git a/app/bean.php b/app/bean.php index f291ace3..9035b412 100644 --- a/app/bean.php +++ b/app/bean.php @@ -131,6 +131,10 @@ 'wsServer' => [ 'class' => WebSocketServer::class, 'port' => 18308, + 'listener' => [ + // 'rpc' => bean('rpcServer'), + // 'tcp' => bean('tcpServer'), + ], 'on' => [ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), @@ -142,16 +146,23 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], + /** @see \Swoft\Tcp\Server\TcpServer */ 'tcpServer' => [ 'port' => 18309, 'debug' => 1, ], /** @see \Swoft\Tcp\Protocol */ 'tcpServerProtocol' => [ - // 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, + // 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, // 'openLengthCheck' => true, ], + /** @see \Swoft\Tcp\Server\TcpDispatcher */ + 'tcpDispatcher' => [ + 'middlewares' => [ + \App\Tcp\Middleware\GlobalTcpMiddleware::class + ], + ], 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], ] From 21feebfef1c0bb60e9e1932f2a077918a40cac53 Mon Sep 17 00:00:00 2001 From: inhere Date: Thu, 24 Oct 2019 10:40:23 +0800 Subject: [PATCH 565/643] up: run php-cs-fixer fix code format --- app/Exception/Handler/HttpExceptionHandler.php | 8 ++++++-- app/Exception/Handler/WsHandshakeExceptionHandler.php | 8 ++++++-- app/Tcp/Middleware/DemoMiddleware.php | 8 ++++++++ app/Tcp/Middleware/GlobalTcpMiddleware.php | 8 ++++++++ 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 4af35438..f4dc48df 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -41,8 +41,12 @@ public function handle(Throwable $e, Response $response): Response // Debug is false if (!APP_DEBUG) { - return $response->withStatus(500)->withContent(sprintf(' %s At %s line %d', $e->getMessage(), $e->getFile(), - $e->getLine())); + return $response->withStatus(500)->withContent(sprintf( + ' %s At %s line %d', + $e->getMessage(), + $e->getFile(), + $e->getLine() + )); } $data = [ diff --git a/app/Exception/Handler/WsHandshakeExceptionHandler.php b/app/Exception/Handler/WsHandshakeExceptionHandler.php index 4b560da7..1a07cf2a 100644 --- a/app/Exception/Handler/WsHandshakeExceptionHandler.php +++ b/app/Exception/Handler/WsHandshakeExceptionHandler.php @@ -35,8 +35,12 @@ public function handle(Throwable $e, Response $response): Response { // Debug is false if (!APP_DEBUG) { - return $response->withStatus(500)->withContent(sprintf('%s At %s line %d', $e->getMessage(), $e->getFile(), - $e->getLine())); + return $response->withStatus(500)->withContent(sprintf( + '%s At %s line %d', + $e->getMessage(), + $e->getFile(), + $e->getLine() + )); } $data = [ diff --git a/app/Tcp/Middleware/DemoMiddleware.php b/app/Tcp/Middleware/DemoMiddleware.php index d6526acf..df76cba3 100644 --- a/app/Tcp/Middleware/DemoMiddleware.php +++ b/app/Tcp/Middleware/DemoMiddleware.php @@ -1,4 +1,12 @@ Date: Thu, 24 Oct 2019 16:57:32 +0800 Subject: [PATCH 566/643] feat: add middleware examples for ws server --- app/WebSocket/Chat/HomeController.php | 3 +- app/WebSocket/Middleware/DemoMiddleware.php | 47 +++++++++++++++++++ .../Middleware/GlobalWsMiddleware.php | 47 +++++++++++++++++++ app/bean.php | 6 +++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 app/WebSocket/Middleware/DemoMiddleware.php create mode 100644 app/WebSocket/Middleware/GlobalWsMiddleware.php diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php index e4f3e3d5..a3e669ae 100644 --- a/app/WebSocket/Chat/HomeController.php +++ b/app/WebSocket/Chat/HomeController.php @@ -10,6 +10,7 @@ namespace App\WebSocket\Chat; +use App\WebSocket\Middleware\DemoMiddleware; use Swoft\Session\Session; use Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping; use Swoft\WebSocket\Server\Annotation\Mapping\WsController; @@ -17,7 +18,7 @@ /** * Class HomeController * - * @WsController() + * @WsController(middlewares={DemoMiddleware::class}) */ class HomeController { diff --git a/app/WebSocket/Middleware/DemoMiddleware.php b/app/WebSocket/Middleware/DemoMiddleware.php new file mode 100644 index 00000000..6449ac59 --- /dev/null +++ b/app/WebSocket/Middleware/DemoMiddleware.php @@ -0,0 +1,47 @@ +before '; + + CLog::info('before handle'); + + $resp = $handler->handle($request); + + $resp->setData($start . $resp->getData() . ' after>'); + + CLog::info('after handle'); + + return $resp; + } +} diff --git a/app/WebSocket/Middleware/GlobalWsMiddleware.php b/app/WebSocket/Middleware/GlobalWsMiddleware.php new file mode 100644 index 00000000..c65ed5c9 --- /dev/null +++ b/app/WebSocket/Middleware/GlobalWsMiddleware.php @@ -0,0 +1,47 @@ +before '; + + CLog::info('before handle'); + + $resp = $handler->handle($request); + + $resp->setData($start . $resp->getData() . ' after>'); + + CLog::info('after handle'); + + return $resp; + } +} diff --git a/app/bean.php b/app/bean.php index 9035b412..238d4ebc 100644 --- a/app/bean.php +++ b/app/bean.php @@ -146,6 +146,12 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], + /** @see \Swoft\WebSocket\Server\WsMessageDispatcher */ + 'wsMsgDispatcher' => [ + 'middlewares' => [ + \App\WebSocket\Middleware\GlobalWsMiddleware::class + ], + ], /** @see \Swoft\Tcp\Server\TcpServer */ 'tcpServer' => [ 'port' => 18309, From 53d5dc334cb6a5d7953a45aa7325ea72c74832b8 Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 1 Nov 2019 17:17:18 +0800 Subject: [PATCH 567/643] up: upgrade redis ext to 5.1.0 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f36550e9..56e5bd55 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ - PHPREDIS_VERSION=4.3.0 \ + PHPREDIS_VERSION=5.1.0 \ SWOOLE_VERSION=4.4.8 \ COMPOSER_ALLOW_SUPERUSER=1 From 24db5cfaa62c290257bbdf4d1d75f3cdfe7cda4e Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 4 Nov 2019 23:10:43 +0800 Subject: [PATCH 568/643] up: dont report error file on app_debug is close --- app/AutoLoader.php | 2 +- app/Exception/Handler/HttpExceptionHandler.php | 7 +------ app/WebSocket/Chat/HomeController.php | 3 +-- app/WebSocket/Test/TestController.php | 3 ++- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/AutoLoader.php b/app/AutoLoader.php index cb4d1b52..1218cac9 100644 --- a/app/AutoLoader.php +++ b/app/AutoLoader.php @@ -32,7 +32,7 @@ public function getPrefixDirs(): array /** * @return array */ - public function metadata(): array + protected function metadata(): array { return []; } diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index f4dc48df..4c50f648 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -41,12 +41,7 @@ public function handle(Throwable $e, Response $response): Response // Debug is false if (!APP_DEBUG) { - return $response->withStatus(500)->withContent(sprintf( - ' %s At %s line %d', - $e->getMessage(), - $e->getFile(), - $e->getLine() - )); + return $response->withStatus(500)->withContent($e->getMessage()); } $data = [ diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php index a3e669ae..e4f3e3d5 100644 --- a/app/WebSocket/Chat/HomeController.php +++ b/app/WebSocket/Chat/HomeController.php @@ -10,7 +10,6 @@ namespace App\WebSocket\Chat; -use App\WebSocket\Middleware\DemoMiddleware; use Swoft\Session\Session; use Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping; use Swoft\WebSocket\Server\Annotation\Mapping\WsController; @@ -18,7 +17,7 @@ /** * Class HomeController * - * @WsController(middlewares={DemoMiddleware::class}) + * @WsController() */ class HomeController { diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index 960dc67b..bebd22ed 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -10,6 +10,7 @@ namespace App\WebSocket\Test; +use App\WebSocket\Middleware\DemoMiddleware; use Swoft\Session\Session; use Swoft\WebSocket\Server\Annotation\Mapping\MessageMapping; use Swoft\WebSocket\Server\Annotation\Mapping\WsController; @@ -24,7 +25,7 @@ /** * Class HomeController * - * @WsController() + * @WsController(middlewares={DemoMiddleware::class}) */ class TestController { From f991da3f6a7a7d88e392e2730af11ee8b8e92dab Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 4 Nov 2019 23:12:48 +0800 Subject: [PATCH 569/643] update some --- app/Exception/Handler/HttpExceptionHandler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Exception/Handler/HttpExceptionHandler.php b/app/Exception/Handler/HttpExceptionHandler.php index 4c50f648..615a45fc 100644 --- a/app/Exception/Handler/HttpExceptionHandler.php +++ b/app/Exception/Handler/HttpExceptionHandler.php @@ -35,9 +35,9 @@ class HttpExceptionHandler extends AbstractHttpErrorHandler */ public function handle(Throwable $e, Response $response): Response { - // Log + // Log error message Log::error($e->getMessage()); - CLog::error($e->getMessage()); + CLog::error('%s. (At %s line %d)', $e->getMessage(), $e->getFile(), $e->getLine()); // Debug is false if (!APP_DEBUG) { From 325ee5be73ca17945243530be831b3a9430b4b62 Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 6 Nov 2019 00:33:30 +0800 Subject: [PATCH 570/643] up: add http controller for session usage examples --- app/Console/Command/TestCommand.php | 3 +- app/Http/Controller/CookieController.php | 56 +++++++++++++++ app/Http/Controller/HomeController.php | 3 - app/Http/Controller/RespController.php | 19 +++-- app/Http/Controller/RpcController.php | 2 - app/Http/Controller/SessionController.php | 85 +++++++++++++++++++++++ app/Http/Controller/ViewController.php | 20 ------ app/bean.php | 1 + 8 files changed, 156 insertions(+), 33 deletions(-) create mode 100644 app/Http/Controller/CookieController.php create mode 100644 app/Http/Controller/SessionController.php diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index 7ad6b094..c82cabae 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -31,7 +31,7 @@ class TestCommand /** * @CommandMapping(name="ab") */ - public function ab() + public function ab(): void { $type = input()->get('type', ''); $uris = $this->uris(); @@ -40,6 +40,7 @@ public function ab() if (empty($type)) { $exeUris = []; foreach ($uris as $name => $uriAry) { + /** @noinspection SlowArrayOperationsInLoopInspection */ $exeUris = array_merge($exeUris, $uriAry); } } else { diff --git a/app/Http/Controller/CookieController.php b/app/Http/Controller/CookieController.php new file mode 100644 index 00000000..483335ce --- /dev/null +++ b/app/Http/Controller/CookieController.php @@ -0,0 +1,56 @@ +getResponse(); + + return $resp->setCookie('c-name', 'c-value')->withData(['hello']); + } + + /** + * @RequestMapping() + * + * @param Request $request + * + * @return array + */ + public function get(Request $request): array + { + return $request->getCookieParams(); + } + + /** + * @RequestMapping("del") + * + * @return Response + */ + public function del(): Response + { + /** @var Response $resp */ + $resp = context()->getResponse(); + + return $resp->delCookie('c-name')->withData(['ok']); + } +} diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 31fa7a09..5e37a48e 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -11,7 +11,6 @@ namespace App\Http\Controller; use Swoft; -use Swoft\Exception\SwoftException; use Swoft\Http\Message\ContentType; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; @@ -43,7 +42,6 @@ public function index(): Response * @RequestMapping("/hi") * * @return Response - * @throws SwoftException */ public function hi(): Response { @@ -55,7 +53,6 @@ public function hi(): Response * @param string $name * * @return Response - * @throws SwoftException */ public function hello(string $name): Response { diff --git a/app/Http/Controller/RespController.php b/app/Http/Controller/RespController.php index 32daa46d..98a4e6b1 100644 --- a/app/Http/Controller/RespController.php +++ b/app/Http/Controller/RespController.php @@ -10,7 +10,6 @@ namespace App\Http\Controller; -use Swoft\Exception\SwoftException; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; @@ -27,14 +26,20 @@ class RespController /** * @RequestMapping() * - * @return Response - * @throws SwoftException + * @return array */ - public function cookie(): Response + public function ary(): array { - /** @var Response $resp */ - $resp = context()->getResponse(); + return ['ary']; + } - return $resp->setCookie('c-name', 'c-value')->withData(['hello']); + /** + * @RequestMapping() + * + * @return string + */ + public function str(): string + { + return 'string'; } } diff --git a/app/Http/Controller/RpcController.php b/app/Http/Controller/RpcController.php index 4fb2f9bc..b92273a2 100644 --- a/app/Http/Controller/RpcController.php +++ b/app/Http/Controller/RpcController.php @@ -13,7 +13,6 @@ use App\Rpc\Lib\UserInterface; use Exception; use Swoft\Co; -use Swoft\Exception\SwoftException; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Rpc\Client\Annotation\Mapping\Reference; @@ -86,7 +85,6 @@ public function bigString(): array * @RequestMapping() * * @return array - * @throws SwoftException */ public function sendBigString(): array { diff --git a/app/Http/Controller/SessionController.php b/app/Http/Controller/SessionController.php new file mode 100644 index 00000000..c6576e1c --- /dev/null +++ b/app/Http/Controller/SessionController.php @@ -0,0 +1,85 @@ +get('times', 0); + $times++; + + $sess->set('times', $times); + + return $response->withData(['times' => $times]); + } + + /** + * @RequestMapping() + * @param Response $response + * + * @return Response + */ + public function set(Response $response): Response + { + $sess = HttpSession::current(); + $sess->set('testKey', 'test-value'); + + return $response->withData(['set.testKey' => 'test-value']); + } + + /** + * @RequestMapping() + * + * @return array + */ + public function get(): array + { + $sess = HttpSession::current(); + + return ['get.testKey' => $sess->get('testKey')]; + } + + /** + * @RequestMapping("del") + * + * @return array + */ + public function del(): array + { + $sess = HttpSession::current(); + $sess->set('testKey', 'test-value'); + + return ['set.testKey' => 'test-value']; + } + + /** + * @RequestMapping() + * @param Response $response + * + * @return Response + */ + public function close(Response $response): Response + { + $sess = HttpSession::current(); + + return $response->withData(['destroy' => $sess->destroy()]); + } +} diff --git a/app/Http/Controller/ViewController.php b/app/Http/Controller/ViewController.php index e5548e14..b8bea1d3 100644 --- a/app/Http/Controller/ViewController.php +++ b/app/Http/Controller/ViewController.php @@ -54,24 +54,4 @@ public function indexByViewTag(): array 'msg' => 'hello' ]; } - - /** - * @RequestMapping() - * - * @return array - */ - public function ary(): array - { - return ['ary']; - } - - /** - * @RequestMapping() - * - * @return string - */ - public function str(): string - { - return 'string'; - } } diff --git a/app/bean.php b/app/bean.php index 238d4ebc..719218da 100644 --- a/app/bean.php +++ b/app/bean.php @@ -62,6 +62,7 @@ // Add global http middleware 'middlewares' => [ \App\Http\Middleware\FavIconMiddleware::class, + \Swoft\Http\Session\SessionMiddleware::class, // \Swoft\Whoops\WhoopsMiddleware::class, // Allow use @View tag \Swoft\View\Middleware\ViewMiddleware::class, From 4591f647a27e67d7d24211d2a38251bae74e86f1 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sun, 17 Nov 2019 16:38:26 +0800 Subject: [PATCH 571/643] fix redis demo --- app/Http/Controller/RedisController.php | 12 ++++++------ app/Listener/{RanListener.php => DbRanListener.php} | 2 +- app/Listener/UserSavingListener.php | 10 ++++------ app/bean.php | 1 + 4 files changed, 12 insertions(+), 13 deletions(-) rename app/Listener/{RanListener.php => DbRanListener.php} (94%) diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index 9b8fa20c..3bd81df6 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -60,18 +60,18 @@ public function poolSet(): array */ public function set(): array { - $key = 'key'; - $value = uniqid(); + $key = 'key1'; - $this->redis->zAdd($key, [ + $data = [ 'add' => 11.1, 'score2' => 11.1, 'score3' => 11.21 - ]); + ]; + $this->redis->zAdd($key, $data); - $get = $this->redis->sMembers($key); + $res = Redis::zRangeByScore($key, '11.1', '11.21', ['withscores' => true]); - return [$get, $value]; + return [$res, $res === $data]; } /** diff --git a/app/Listener/RanListener.php b/app/Listener/DbRanListener.php similarity index 94% rename from app/Listener/RanListener.php rename to app/Listener/DbRanListener.php index 242054fd..28e52a2f 100644 --- a/app/Listener/RanListener.php +++ b/app/Listener/DbRanListener.php @@ -23,7 +23,7 @@ * * @Listener(DbEvent::SQL_RAN) */ -class RanListener implements EventHandlerInterface +class DbRanListener implements EventHandlerInterface { /** * SQL ran diff --git a/app/Listener/UserSavingListener.php b/app/Listener/UserSavingListener.php index c0c04528..fbe9be42 100644 --- a/app/Listener/UserSavingListener.php +++ b/app/Listener/UserSavingListener.php @@ -34,12 +34,10 @@ public function handle(EventInterface $event): void $user = $event->getTarget(); /** - if ($user->getAge() > 100) { - // stopping saving - $event->stopPropagation(true); - - $user->setAdd(100); - } + * if ($user->getAge() > 100) { + * // stopping saving + * $event->stopPropagation(true); + * } */ } } diff --git a/app/bean.php b/app/bean.php index f291ace3..439f7eb5 100644 --- a/app/bean.php +++ b/app/bean.php @@ -75,6 +75,7 @@ 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', + 'charset' => 'utf8mb4', ], 'db2' => [ 'class' => Database::class, From 1b2a626bdd88ea45ec07d450d119e354c4fcfd03 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 17 Nov 2019 20:25:36 +0800 Subject: [PATCH 572/643] up --- app/Http/Controller/SessionController.php | 30 ++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/app/Http/Controller/SessionController.php b/app/Http/Controller/SessionController.php index c6576e1c..357691dd 100644 --- a/app/Http/Controller/SessionController.php +++ b/app/Http/Controller/SessionController.php @@ -31,6 +31,18 @@ public function session(Response $response): Response return $response->withData(['times' => $times]); } + /** + * @RequestMapping("all") + * + * @return array + */ + public function all(): array + { + $sess = HttpSession::current(); + + return $sess->toArray(); + } + /** * @RequestMapping() * @param Response $response @@ -41,8 +53,9 @@ public function set(Response $response): Response { $sess = HttpSession::current(); $sess->set('testKey', 'test-value'); + $sess->set('testKey1', ['k' => 'v', 'v1', 3]); - return $response->withData(['set.testKey' => 'test-value']); + return $response->withData(['testKey', 'testKey1']); } /** @@ -82,4 +95,19 @@ public function close(Response $response): Response return $response->withData(['destroy' => $sess->destroy()]); } + + // ------------ flash session usage + + /** + * @RequestMapping() + * + * @return array + */ + public function flash(): array + { + $sess = HttpSession::current(); + $sess->setFlash('flash1', 'test-value'); + + return ['set.testKey' => 'test-value']; + } } From b6fd5e5970b3c5bc176c36aee0dafa8633429d8a Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 17 Nov 2019 20:27:11 +0800 Subject: [PATCH 573/643] fix cs error --- app/Http/Controller/CookieController.php | 8 ++++++++ app/Http/Controller/RespController.php | 1 - app/Http/Controller/SessionController.php | 8 ++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/app/Http/Controller/CookieController.php b/app/Http/Controller/CookieController.php index 483335ce..4dcde82c 100644 --- a/app/Http/Controller/CookieController.php +++ b/app/Http/Controller/CookieController.php @@ -1,4 +1,12 @@ Date: Mon, 18 Nov 2019 00:49:50 +0800 Subject: [PATCH 574/643] up: add http session component --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index d9c1185c..7a76f90a 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "swoft/limiter": "~2.0.0", "swoft/breaker": "~2.0.0", "swoft/crontab": "~2.0.0", + "swoft/session": "~2.0.0", "swoft/devtool": "~2.0.0" }, "require-dev": { From ff91f1339dd886184d83793cc3a75235613a511a Mon Sep 17 00:00:00 2001 From: Andrey Bolonin Date: Sun, 1 Dec 2019 10:07:10 +0200 Subject: [PATCH 575/643] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 70ca9ccb..bab6451d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ php: - 7.1 - 7.2 - 7.3 + - 7.4 services: - redis From 378ff4b2f8ec477cac7a84f556d8680472a54e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Tue, 3 Dec 2019 17:30:51 +0800 Subject: [PATCH 576/643] Update README.zh-CN.md --- README.zh-CN.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.zh-CN.md b/README.zh-CN.md index 4a5eb12a..2a6472af 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -66,6 +66,10 @@ Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP - QQ Group2: 778656850 - [swoft-cloud/community](https://gitter.im/swoft-cloud/community) +## 免费技术支持 + +![support](https://www.swoft.org/src/images/technical-support.png) + ## Requirement - [PHP 7.1+](https://github.com/php/php-src/releases) From 6da23fbbea4df0efd474ff959972192f598dab54 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 18 Dec 2019 11:37:32 +0800 Subject: [PATCH 577/643] Create label.yml --- .github/workflows/label.yml | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .github/workflows/label.yml diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml new file mode 100644 index 00000000..64985ffc --- /dev/null +++ b/.github/workflows/label.yml @@ -0,0 +1,38 @@ +# This workflow will triage pull requests and apply a label based on the +# paths that are modified in the pull request. +# +# To use this workflow, you will need to set up a .github/labeler.yml +# file with configuration. For more information, see: +# https://github.com/actions/labeler/blob/master/README.md + +name: Issue Labeler +on: + issues: + types: [opened] + +jobs: + label: + runs-on: ubuntu-latest + steps: + - name: Db Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + # Github token + github-token: "${{ secrets.GITHUB_TOKEN }}" + # keywords to look for in the issue + keywords: '["db", "database", "mysql", "pdo"]' + # assignees to be assigned when keyword is found + assignees: '["sakuraovq"]' + # labels to be set when keyword is found + labels: '["swoft: db"]' # optional + - name: Websocket Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + # Github token + github-token: "${{ secrets.GITHUB_TOKEN }}" + # keywords to look for in the issue + keywords: '["websocket", "websocket-server", "ws-server"]' + # assignees to be assigned when keyword is found + assignees: '["inhere"]' + # labels to be set when keyword is found + labels: '["swoft: websocket"]' # optional From 3e87902946047b5658afaabd283e21d837064ff5 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 18 Dec 2019 18:23:37 +0800 Subject: [PATCH 578/643] Update README.zh-CN.md --- README.zh-CN.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.zh-CN.md b/README.zh-CN.md index 2a6472af..6d3eab76 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -104,6 +104,51 @@ composer create-project swoft/swoft swoft [root@swoft swoft]# php bin/swoft rpc:start ``` +## 核心组件 + +Component Name | Packagist Version +--------------------|--------------------- +swoft-annotation | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/annotation.svg)](https://packagist.org/packages/swoft/annotation) +swoft-config | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/config.svg)](https://packagist.org/packages/swoft/config) +swoft-db | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/db.svg)](https://packagist.org/packages/swoft/db) +swoft-framework | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/framework.svg)](https://packagist.org/packages/swoft/framework) +swoft-i18n | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/i18n.svg)](https://packagist.org/packages/swoft/i18n) +swoft-proxy | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/proxy.svg)](https://packagist.org/packages/swoft/proxy) +swoft-rpc-client | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-client.svg)](https://packagist.org/packages/swoft/rpc-client) +swoft-stdlib | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/stdlib.svg)](https://packagist.org/packages/swoft/stdlib) +swoft-tcp-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp-server.svg)](https://packagist.org/packages/swoft/tcp-server) +swoft-aop | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/aop.svg)](https://packagist.org/packages/swoft/aop) +swoft-connection-pool | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/connection-pool.svg)](https://packagist.org/packages/swoft/connection-pool) +swoft-error | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/error.svg)](https://packagist.org/packages/swoft/error) +swoft-http-message | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-message.svg)](https://packagist.org/packages/swoft/http-message) +swoft-log | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/log.svg)](https://packagist.org/packages/swoft/log) +swoft-redis | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/redis.svg)](https://packagist.org/packages/swoft/redis) +swoft-rpc-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc-server.svg)](https://packagist.org/packages/swoft/rpc-server) +swoft-task | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/task.svg)](https://packagist.org/packages/swoft/task) +swoft-validator | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/validator.svg)](https://packagist.org/packages/swoft/validator) +swoft-bean | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/bean.svg)](https://packagist.org/packages/swoft/bean) +swoft-console | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/console.svg)](https://packagist.org/packages/swoft/console) +swoft-event | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/event.svg)](https://packagist.org/packages/swoft/event) +swoft-http-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/http-server.svg)](https://packagist.org/packages/swoft/http-server) +swoft-process | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/process.svg)](https://packagist.org/packages/swoft/process) +swoft-rpc | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/rpc.svg)](https://packagist.org/packages/swoft/rpc) +swoft-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/server.svg)](https://packagist.org/packages/swoft/server) +swoft-tcp | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/tcp.svg)](https://packagist.org/packages/swoft/tcp) +swoft-websocket-server | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/websocket-server.svg)](https://packagist.org/packages/swoft/websocket-server) + +## 扩展组件 + +Component Name | Packagist Version +-----------------|--------------------- +swoft-apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/apollo.svg)](https://packagist.org/packages/swoft/apollo) +swoft-breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/breaker.svg)](https://packagist.org/packages/swoft/breaker) +swoft-crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/crontab.svg)](https://packagist.org/packages/swoft/crontab) +swoft-consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/consul.svg)](https://packagist.org/packages/swoft/consul) +swoft-limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/limiter.svg)](https://packagist.org/packages/swoft/limiter) +swoft-view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/view.svg)](https://packagist.org/packages/swoft/view) +swoft-whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/whoops.svg)](https://packagist.org/packages/swoft/whoops) +swoft-session | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/session.svg)](https://packagist.org/packages/swoft/session) + ## License Swoft is an open-source software licensed under the [LICENSE](LICENSE) From f6e0e6af0e6b1f36ae6e3cf028be5c0ead938526 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 20 Dec 2019 12:47:19 +0800 Subject: [PATCH 579/643] up: add more examples, add some new config --- app/Http/Controller/CacheController.php | 71 ++++++++++++++++++++++ app/Http/Controller/SessionController.php | 19 +++++- app/bean.php | 12 +++- public/favicon.ico | Bin 0 -> 9662 bytes 4 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 app/Http/Controller/CacheController.php create mode 100644 public/favicon.ico diff --git a/app/Http/Controller/CacheController.php b/app/Http/Controller/CacheController.php new file mode 100644 index 00000000..b39a4399 --- /dev/null +++ b/app/Http/Controller/CacheController.php @@ -0,0 +1,71 @@ + $ok, 'ckey1' => $ok1]; + } + + /** + * @RequestMapping() + * + * @return array + * @throws InvalidArgumentException + */ + public function get(): array + { + $val = Cache::get('ckey'); + + return [ + 'ckey' => $val, + 'ckey1' => Cache::get('ckey1') + ]; + } + + /** + * @RequestMapping("del") + * + * @return array + * @throws InvalidArgumentException + */ + public function del(): array + { + /** @var Response $resp */ + // $resp = context()->getResponse(); + + return ['del' => Cache::delete('ckey')]; + } +} diff --git a/app/Http/Controller/SessionController.php b/app/Http/Controller/SessionController.php index 8287d8ec..4feab237 100644 --- a/app/Http/Controller/SessionController.php +++ b/app/Http/Controller/SessionController.php @@ -36,7 +36,10 @@ public function session(Response $response): Response $sess->set('times', $times); - return $response->withData(['times' => $times]); + return $response->withData([ + 'times' => $times, + 'sessId' => $sess->getSessionId() + ]); } /** @@ -86,9 +89,9 @@ public function get(): array public function del(): array { $sess = HttpSession::current(); - $sess->set('testKey', 'test-value'); + $ok = $sess->delete('testKey'); - return ['set.testKey' => 'test-value']; + return ['delete' => $ok]; } /** @@ -104,6 +107,16 @@ public function close(Response $response): Response return $response->withData(['destroy' => $sess->destroy()]); } + /** + * @RequestMapping() + * + * @return string + */ + public function not(): string + { + return 'not-use'; + } + // ------------ flash session usage /** diff --git a/app/bean.php b/app/bean.php index 5d65e6b9..64e2d926 100644 --- a/app/bean.php +++ b/app/bean.php @@ -40,7 +40,9 @@ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ - 'rpc' => bean('rpcServer') + // 'rpc' => bean('rpcServer'), + // 'tcp' => bean('tcpServer'), + // 'ws' => bean('wsServer') ], 'process' => [ // 'monitor' => bean(MonitorProcess::class) @@ -173,5 +175,11 @@ ], 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], - ] + ], + 'wsConnectionManager' => [ + 'storage' => bean('wsConnectionStorage') + ], + 'wsConnectionStorage' => [ + 'class' => \Swoft\Session\SwooleStorage::class, + ], ]; diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d6fe12dc2ec62a2bff81e8728ad9ea213561dcbe GIT binary patch literal 9662 zcmeHM2UwI>x<13G5SPS_ZgS&Q0|EmK(_n@&L$40K2#81*DJn=6!H6_PMKHt`K?D^n zfS|Df8WT(`DAFt-Qp6HVNL;hK&%Jl=s@(Vdlt&@vUd&B)v(Fx$;rw&{^PTg4-}&kp zLRk3I)g}0^N)B=ed7luXjSq%MFg|pgX&Qt~{oUYaaNuA6|5+tab>t{HcCwbVv|S|w z0|UP)Ht#=pK)k%?k6@0COZ?67P`Gm!gF9kL)CY4t=efGLc$j@!u=AZ6GgQde7cP$Q z^pE!Nr$7IhBrZ=Ttyixpdw6)e>g!97s?Fy7r-r(Azln+YcK?82DfEcFb^DHgsI$0u z`3f+hs8Ar#>(n=pUE*kPzt_^@-O&>pHe#&R%*<-_ru@w+)E<^Z z#r$3Qpco2?x~{FoduVKGxq8FKJSm>9X=?Ef1FKM^t6$2|)cH;* z6rT>Bobe@wR1>X8Xc=b-)-6UDeZohcA)$a zvrlwfBFD)#mpa8ky|%4-B#B z>gt|I7s$w5`~SiV-LH7>Af}|`l}gYJJ3T#vDjgkRud#{6zIi@=rZ;>0-tzSe9J3$r z&(MVaQ2g3DLI(dZb4QLIXC5=M)=qPFcJqVHp9dc|tZn4$6B3iu{|Z0l?c+yo-?=+X zGVZ>srtz*{~+(Iwd>xYi{|BT9)q2Ze}3kR_ciJ*8{H_bwb|>L;R_9y*vn!Q48>x@A^~5o)4Xu`TqHtFQ*{} zRjaC~zu@si&56rX%&{Mf#-DHd`^PMC32^{El#$hrf}#)XdZBhgMvftB>F~2pnLr)D(cTcdq60X zykb8igg^0c^w!ilNiXxjh~)%{+4ZI(p&G4!je6#HS0EL$c)Xh z5PPnQbR`$~U(=z!eM+s<)Zy#HYNdz2nNk~lfLGdgwPV8wfg zSxe{~%)JhOuyWPvbglHvwH!-Jn5 zoV<@IjtR)ajHOc3VxdrS7yH%e>gqRO?;gl5Jw9;-m(qQ9Y|uZxyaqi_+Ts(ExWJwX znl8{@(9_jFhv#lnQ_FRcOPBHKoYMUV$;p~p#6R93{x^_6UFYRiP*hBaf+E#>#n7-w zf8f8Ts-p2%RaK3@85qhMGqSP`!JmSq^&E1s;nK^b%rnKrR!|~)O3Nn2B`kM@pJ`W7 zW$6D4XVMN`aw z!mxmaq2YcwgHv2>LAQ;K{9_+qe*rz`vtON?j(o2bd+mqqk6VA}zI)5|GZ^BRC#%`m z%9D9~-RoHEl9jDI0X50=hlm4>P0b1k%Tt7=W|o_1J^?$ecXV>~+gDyWEi7y?L%%95 zLuHQKDIPgT8~i{E{AtR@ynL08u5Q-d2M@{St=nhXSj$&HpPB_iaVvap+`0|9#*qJ2 z)DTtZ$Bn+HGnldWy-$&L1o(d4AP<0hHo;7l%FZk&wV#~(7g z|KW#f0~%_Ym6{sb;Z|`u)#kl#Id}pTG>cuPOh}HjoBai#EeBP zi=72KNEHh79wGm~t*xo^5Ps^G$6W8Av^Ck&=%f?ib#yDlyyQ{6m z?=&^Dj&_~vJ?{8hd8nGB!xdbI{ZM~@k}uTjg^q6x2nuEDSk!^cemHls5VcI*>CdOm zkvsYt8JV2X;R(B>#%77k+Yf~^z`zvrMfnxb5w7b7}#+6$`pGC z7Z3R66Uc9SkT-l9wJc8h)z|0QQK+lO+bSxnIXoTFHLSC1ReC1u&oLUd)eN~@MV^zo zJ|}l}>AnN3?(UvR!6A#RQ3IE8xxzkUWAk!XcTa~O{_>Z%R;H$ru<*sC>|n)I2S?Wh z`Vwh9^2?iuce#m4Dg25<)hy`EgvcdPG7}SvLYkKw85ti%PVIK}#tr3|pKe6}q*O`dX~r=;iI30$s_5@4T)jHn{BM>=q8p)9K`0yZ4OHzsbW0ez9S( zxup#Y`a~r9GRnuqpx{M8!0-Tm;W<77sGn-{KHkiu`_O!*??>v63k{bE;s@hrP|rJn z|IXQ}oWJ4oocdhUTw+R(m_j-_Z_&q})n3A1!sUrrG`DTNc0(ER@<82slE#1d^GfJb zJ)Upj{PVE482qtzmAooDs^iO(KO+54{x}&qy(8jv1!!)=M^=Eo271{mlbLQq4a~&w z9fd_d3p*tq#;+?j*md#J<%y7)6#EuX+>qC2@Fy5 zj~W5_E~%+I{YJc0R#tIfPuC#p6UZAgs4ZacM&L#|vFro7XzBg~1o<)5i3zAFMW|ml z!8W>4&z|<4=Q}SZCjR&M%(8cIPC+bbrSaR{!#fUlFCS8R>T?mB8QVhM&j%lQu!Uaa zMqhaQ`1;^piK$UhYt!lDYKN*0bF{SiHxd8-8!_mPt)2Z^+O->#^?k9_vE~7Q|?;V}omT7AX z9)kCKR@QdWCT3RSj$h1P8EaG_gGJDXGw|CTQkm)c%r)6M$ZwhTic1D#=4dd-j-N#S zTc-*i)oN~OyE$}W_>b~T<$yaBh8*?tMBuj&ON{n{=Wh74N)OcUv>u*5Q+4zf|LgZB zPk&(Ulj+3ph$XX8ZzUm~T}K|);OOkOWPMH^Shk|@(r-Bc3^LG^Y99St}*u~^iJ%@&U?&;uNnCA>^U-9O^Y0> zshb!Ro1hC@+6lkV1Nj_BeduuH*zvb@#fGG_tLv2&s;h_aTiI<{;2-=7{-PoOy5gf% zA2_?XN5Wp4VD~rX*gK^mE~^5s!u$n+lM`|W7xxR78{dgs68(!|9xVroam8TGq7W1kriUEw zApBiF_^4YL7GZ?ni6+$6o|=u{11NsxP|`EkC?l^62kv(Gvu}`#u3nzJat2*{XYuaQ zTl^~D50qCj`)u7_@ScN%OAOT)K3{Z$KCenmv!XH2!qToNWMP;TcMdzj&mH6nHNhca z&WO=UGE)n3`AXBTg7tNq?Ag!!JVB11s9~k1WeAaD?Z{hO5%-=TC!lw49Rh*) zf?VztQ?zS0^BaKxxy3J~A2=eyy$J|;HteK&VcTjWl{o87%i^L+iuecYM<78*SZ-1RU}C*wXU zCzrXq`z=5p!FZ{unKZVvlJ47mBV+%1_~(-Vbd{!EbuV)YT=`5yy zK`fd6*#uP!x=!Cr=@i=6uQF%Tp7qRvh5i})nMHjX`YBJRJhect*r(Ws{sk+Ym5zP@ z9ie^Sl&8wH$26v|SFC5ohu%M9*R$*Keq^2D^@hg@797BPOKd=UU6rRQv{xPaKK!1Y z&Q7P}LpZRW3dQw;{Va{*9LN~NJ;KM+A-;zB9OfIq8U6=` literal 0 HcmV?d00001 From 8f9e816b1389a9556f4c099e7d09c19977455e04 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 20 Dec 2019 12:49:13 +0800 Subject: [PATCH 580/643] up: change session.mustGet to session.current --- app/WebSocket/Chat/HomeController.php | 4 ++-- app/WebSocket/EchoModule.php | 2 +- app/WebSocket/Test/TestController.php | 16 ++++++++-------- app/WebSocket/TestModule.php | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/WebSocket/Chat/HomeController.php b/app/WebSocket/Chat/HomeController.php index e4f3e3d5..35a730be 100644 --- a/app/WebSocket/Chat/HomeController.php +++ b/app/WebSocket/Chat/HomeController.php @@ -29,7 +29,7 @@ class HomeController */ public function index(): void { - Session::mustGet()->push('hi, this is home.index'); + Session::current()->push('hi, this is home.index'); } /** @@ -40,7 +40,7 @@ public function index(): void */ public function echo(string $data): void { - Session::mustGet()->push('(home.echo)Recv: ' . $data); + Session::current()->push('(home.echo)Recv: ' . $data); } /** diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php index 93a1f92f..1c97cf0b 100644 --- a/app/WebSocket/EchoModule.php +++ b/app/WebSocket/EchoModule.php @@ -33,7 +33,7 @@ class EchoModule */ public function onOpen(Request $request, int $fd): void { - Session::mustGet()->push("Opened, welcome #{$fd}!"); + Session::current()->push("Opened, welcome #{$fd}!"); } /** diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index bebd22ed..e1685a10 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -37,7 +37,7 @@ class TestController */ public function index(): void { - Session::mustGet()->push('hi, this is test.index'); + Session::current()->push('hi, this is test.index'); } /** @@ -51,7 +51,7 @@ public function close(Message $msg): void { $data = $msg->getData(); /** @var Connection $conn */ - $conn = Session::mustGet(); + $conn = Session::current(); $fd = is_numeric($data) ? (int)$data : $conn->getFd(); @@ -73,7 +73,7 @@ public function injectRequest(Request $req): void { $fd = $req->getFd(); - Session::mustGet()->push("(your FD: $fd)message data: " . json_encode($req->getMessage()->toArray())); + Session::current()->push("(your FD: $fd)message data: " . json_encode($req->getMessage()->toArray())); } /** @@ -86,7 +86,7 @@ public function injectRequest(Request $req): void */ public function injectMessage(Message $msg): void { - Session::mustGet()->push('message data: ' . json_encode($msg->toArray())); + Session::current()->push('message data: ' . json_encode($msg->toArray())); } /** @@ -97,7 +97,7 @@ public function injectMessage(Message $msg): void */ public function echo(string $data): void { - Session::mustGet()->push('(echo)Recv: ' . $data); + Session::current()->push('(echo)Recv: ' . $data); } /** @@ -113,7 +113,7 @@ public function hi(Request $req, Response $res): void $ufd = (int)$req->getMessage()->getData(); if ($ufd < 1) { - Session::mustGet()->push('data must be an integer'); + Session::current()->push('data must be an integer'); return; } @@ -130,7 +130,7 @@ public function hi(Request $req, Response $res): void */ public function binary(string $data): string { - // Session::mustGet()->push('Binary: ' . $data, \WEBSOCKET_OPCODE_BINARY); + // Session::current()->push('Binary: ' . $data, \WEBSOCKET_OPCODE_BINARY); return 'Binary: ' . $data; } @@ -141,7 +141,7 @@ public function binary(string $data): string */ public function pong(): void { - Session::mustGet()->push('pong!', WEBSOCKET_OPCODE_PONG); + Session::current()->push('pong!', WEBSOCKET_OPCODE_PONG); } /** diff --git a/app/WebSocket/TestModule.php b/app/WebSocket/TestModule.php index fed7e97d..a282b85d 100644 --- a/app/WebSocket/TestModule.php +++ b/app/WebSocket/TestModule.php @@ -36,6 +36,6 @@ class TestModule */ public function onOpen(Request $request, int $fd): void { - Session::mustGet()->push("Opened, welcome!(FD: $fd)"); + Session::current()->push("Opened, welcome!(FD: $fd)"); } } From 82a3a24d9c02919a9eb8c0f13b2bf0d01f558383 Mon Sep 17 00:00:00 2001 From: shiyifei Date: Thu, 26 Dec 2019 16:45:11 +0800 Subject: [PATCH 581/643] request support get/post method, but only post request can be validated correctly --- app/Http/Controller/ValidatorController.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/app/Http/Controller/ValidatorController.php b/app/Http/Controller/ValidatorController.php index a0f6a95f..bb0a61af 100644 --- a/app/Http/Controller/ValidatorController.php +++ b/app/Http/Controller/ValidatorController.php @@ -13,6 +13,7 @@ use Swoft\Http\Message\Request; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; +use Swoft\Http\Server\Annotation\Mapping\RequestMethod; use Swoft\Validator\Annotation\Mapping\Validate; /** @@ -34,6 +35,10 @@ class ValidatorController */ public function validateAll(Request $request): array { + $method = $request->getMethod(); + if ($method == RequestMethod::GET) { + return $request->getParsedQuery(); + } return $request->getParsedBody(); } @@ -49,6 +54,10 @@ public function validateAll(Request $request): array */ public function validateType(Request $request): array { + $method = $request->getMethod(); + if ($method == RequestMethod::GET) { + return $request->getParsedQuery(); + } return $request->getParsedBody(); } @@ -64,6 +73,10 @@ public function validateType(Request $request): array */ public function validatePassword(Request $request): array { + $method = $request->getMethod(); + if ($method == RequestMethod::GET) { + return $request->getParsedQuery(); + } return $request->getParsedBody(); } @@ -71,6 +84,7 @@ public function validatePassword(Request $request): array * Customize the validator with userValidator * * @RequestMapping() + * * @Validate(validator="userValidator") * * @param Request $request @@ -79,6 +93,10 @@ public function validatePassword(Request $request): array */ public function validateCustomer(Request $request): array { + $method = $request->getMethod(); + if ($method == RequestMethod::GET) { + return $request->getParsedQuery(); + } return $request->getParsedBody(); } } From c8be05a962d495b32b8c994edf8d77af39d67e00 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 26 Dec 2019 16:58:33 +0800 Subject: [PATCH 582/643] Create PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..5761ac29 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ + + +### What does this PR do? + + + + +### Motivation + + + + +### More + +- [ ] Added/updated tests +- [ ] Added/updated documentation + +### Additional Notes + + From 277944157c3ba11232cd61c1f3478d5e416ad390 Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 27 Dec 2019 14:21:37 +0800 Subject: [PATCH 583/643] upgrade swoole to latest 4.4.14 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 56e5bd55..b8b0f967 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=5.1.0 \ - SWOOLE_VERSION=4.4.8 \ + SWOOLE_VERSION=4.4.14 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From a5b4fcd6f9480bb3a23ef548162a3bbbbc69339e Mon Sep 17 00:00:00 2001 From: Inhere Date: Fri, 27 Dec 2019 19:27:22 +0800 Subject: [PATCH 584/643] Update label.yml --- .github/workflows/label.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 64985ffc..eee2c365 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -25,6 +25,28 @@ jobs: assignees: '["sakuraovq"]' # labels to be set when keyword is found labels: '["swoft: db"]' # optional + - name: Http Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + # Github token + github-token: "${{ secrets.GITHUB_TOKEN }}" + # keywords to look for in the issue + keywords: '["http", "http-server", "http-client", "http-message"]' + # assignees to be assigned when keyword is found + assignees: '["stelin", "inhere"]' + # labels to be set when keyword is found + labels: '["swoft: http"]' # optional + - name: Rpc Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + # Github token + github-token: "${{ secrets.GITHUB_TOKEN }}" + # keywords to look for in the issue + keywords: '["rpc", "rpc-server", "rpc-client"]' + # assignees to be assigned when keyword is found + assignees: '["stelin"]' + # labels to be set when keyword is found + labels: '["swoft: rpc"]' # optional - name: Websocket Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: From a902403b7114c135b85ec83b7066556feaefbe1b Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 9 Jan 2020 18:29:57 +0800 Subject: [PATCH 585/643] Update label.yml --- .github/workflows/label.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index eee2c365..4a7e3a26 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -25,6 +25,17 @@ jobs: assignees: '["sakuraovq"]' # labels to be set when keyword is found labels: '["swoft: db"]' # optional + - name: Event Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + # Github token + github-token: "${{ secrets.GITHUB_TOKEN }}" + # keywords to look for in the issue + keywords: '["event", "trigger"]' + # assignees to be assigned when keyword is found + assignees: '["inhere"]' + # labels to be set when keyword is found + labels: '["swoft: event"]' # optional - name: Http Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: From ad01de7232911f3f12cb52a589e27197d2c35ab0 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 13 Jan 2020 20:14:18 +0800 Subject: [PATCH 586/643] update: add some new demo codes --- app/WebSocket/Test/TestController.php | 14 +++++ app/bean.php | 12 ++-- bin/bootstrap.php | 8 ++- docker-compose.yml | 4 +- test/bootstrap.php | 6 ++ test/run.php | 80 +++++++++++++++++++++++++++ 6 files changed, 116 insertions(+), 8 deletions(-) create mode 100755 test/run.php diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index e1685a10..fd7d7b6f 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -156,4 +156,18 @@ public function autoReply(string $data): string { return '(home.ar)Recv: ' . $data; } + + /** + * Message command is: 'test.ar' + * + * @MessageMapping("stop-worker") + */ + public function testDie(): void + { + $wid = \server()->getPid('workerId'); + + \vdump($wid); + + \server()->stopWorker($wid); + } } diff --git a/app/bean.php b/app/bean.php index 64e2d926..e0f982d9 100644 --- a/app/bean.php +++ b/app/bean.php @@ -150,6 +150,12 @@ 'log_file' => alias('@runtime/swoole.log'), ], ], + // 'wsConnectionManager' => [ + // 'storage' => bean('wsConnectionStorage') + // ], + // 'wsConnectionStorage' => [ + // 'class' => \Swoft\Session\SwooleStorage::class, + // ], /** @see \Swoft\WebSocket\Server\WsMessageDispatcher */ 'wsMsgDispatcher' => [ 'middlewares' => [ @@ -176,10 +182,4 @@ 'cliRouter' => [ // 'disabledGroups' => ['demo', 'test'], ], - 'wsConnectionManager' => [ - 'storage' => bean('wsConnectionStorage') - ], - 'wsConnectionStorage' => [ - 'class' => \Swoft\Session\SwooleStorage::class, - ], ]; diff --git a/bin/bootstrap.php b/bin/bootstrap.php index e45da053..bd8c79bb 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -7,4 +7,10 @@ * @contact group@swoft.org * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ -require_once dirname(__DIR__) . '/vendor/autoload.php'; + +/** @var \Composer\Autoload\ClassLoader $loader */ +$loader = require dirname(__DIR__) . '/vendor/autoload.php'; + +$loader->addPsr4("Swoft\\Cache\\", 'vendor/swoft/cache/src/'); +$loader->addPsr4("Swoft\\Swlib\\", 'vendor/swoft/swlib/src/'); +$loader->addPsr4("Swoft\\Serialize\\", 'vendor/swoft/serialize/src/'); diff --git a/docker-compose.yml b/docker-compose.yml index 6b795d76..17988fb4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,9 @@ services: - "18307:18307" - "18308:18308" volumes: - - ./:/var/www/swoft + - ./:/var/www/swoft +# - ./:/var/www/swoft:delegated +# - ./:/var/www/swoft:cached # - ./runtime/ng-conf:/etc/nginx # - ./runtime/logs:/var/log diff --git a/test/bootstrap.php b/test/bootstrap.php index 1cee8fd8..d29c06a1 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -9,3 +9,9 @@ */ define('APP_DEBUG', 1); + +$vendor = dirname(__DIR__); + +/** @var \Composer\Autoload\ClassLoader $loader */ +$loader = require dirname(__DIR__) . '/vendor/autoload.php'; +$loader->addPsr4("SwoftTest\\Testing\\", $vendor . '/swoft/framework/test/testing/'); diff --git a/test/run.php b/test/run.php new file mode 100755 index 00000000..8069fece --- /dev/null +++ b/test/run.php @@ -0,0 +1,80 @@ + SWOOLE_LOG_INFO, + 'trace_flags' => 0 +]); + +/* + * This file is part of PHPUnit. + * + * (c) Sebastian Bergmann + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +if (version_compare('7.1.0', PHP_VERSION, '>')) { + fwrite(STDERR, + sprintf('This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, + PHP_VERSION, PHP_BINARY)); + die(1); +} + +if (!ini_get('date.timezone')) { + ini_set('date.timezone', 'UTC'); +} + +// add loader file +$__loader_file = dirname(__DIR__) . '/vendor/autoload.php'; +if (file_exists($__loader_file)) { + define('PHPUNIT_COMPOSER_INSTALL', $__loader_file); +} + +if (!defined('PHPUNIT_COMPOSER_INSTALL')) { + fwrite(STDERR, + "You need to set up the project dependencies using Composer:\n\n" + . ' composer install' . PHP_EOL . PHP_EOL + . 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL); + die(1); +} + +if (array_reverse(explode('/', __DIR__))[0] ?? '' === 'test') { + $vendor_dir = dirname(PHPUNIT_COMPOSER_INSTALL); + $bin_unit = "{$vendor_dir}/bin/phpunit"; + $unit_uint = "{$vendor_dir}/phpunit/phpunit/phpunit"; + if (file_exists($bin_unit)) { + @unlink($bin_unit); + @symlink(__FILE__, $bin_unit); + } + if (file_exists($unit_uint)) { + @unlink($unit_uint); + @symlink(__FILE__, $unit_uint); + } +} + +if (!in_array('-c', $_SERVER['argv'], true)) { + $_SERVER['argv'][] = '-c'; + $_SERVER['argv'][] = dirname(__DIR__) . '/phpunit.xml'; +} + +require PHPUNIT_COMPOSER_INSTALL; + +$status = 0; +\Swoft\Co::run(function () { + // Status + global $status; + + try { + $status = Command::main(false); + } catch (ExitException $e) { + $status = $e->getCode(); + echo 'ExitException: ' . $e->getMessage(), "\n"; + } +}); + +exit($status); From 87dc04ad74f1293f8c8b226835cddd90f3b0aaf9 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 13 Jan 2020 22:12:05 +0800 Subject: [PATCH 587/643] add some new file --- test/bootstrap.php | 2 +- test/testing/AutoLoader.php | 33 +++++++++++++++++++++++++++++++++ test/testing/bean.php | 7 +++++++ test/unit/ExampleTest.php | 10 ++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/testing/AutoLoader.php create mode 100644 test/testing/bean.php create mode 100644 test/unit/ExampleTest.php diff --git a/test/bootstrap.php b/test/bootstrap.php index d29c06a1..0c24e283 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -10,7 +10,7 @@ define('APP_DEBUG', 1); -$vendor = dirname(__DIR__); +$vendor = dirname(__DIR__) . '/vendor'; /** @var \Composer\Autoload\ClassLoader $loader */ $loader = require dirname(__DIR__) . '/vendor/autoload.php'; diff --git a/test/testing/AutoLoader.php b/test/testing/AutoLoader.php new file mode 100644 index 00000000..40f2c74c --- /dev/null +++ b/test/testing/AutoLoader.php @@ -0,0 +1,33 @@ + __DIR__, + ]; + } + + /** + * @return array + */ + public function metadata(): array + { + return []; + } +} diff --git a/test/testing/bean.php b/test/testing/bean.php new file mode 100644 index 00000000..0eb0174d --- /dev/null +++ b/test/testing/bean.php @@ -0,0 +1,7 @@ + [ + 'path' => __DIR__ . '/../config', + ] +]; diff --git a/test/unit/ExampleTest.php b/test/unit/ExampleTest.php new file mode 100644 index 00000000..d2f91e6b --- /dev/null +++ b/test/unit/ExampleTest.php @@ -0,0 +1,10 @@ + Date: Wed, 15 Jan 2020 12:49:42 +0800 Subject: [PATCH 588/643] update: add some new file --- test/apitest/.keep | 0 test/bootstrap.php | 19 +++++++++---------- test/testing/bean.php | 7 ------- 3 files changed, 9 insertions(+), 17 deletions(-) create mode 100644 test/apitest/.keep delete mode 100644 test/testing/bean.php diff --git a/test/apitest/.keep b/test/apitest/.keep new file mode 100644 index 00000000..e69de29b diff --git a/test/bootstrap.php b/test/bootstrap.php index 0c24e283..cd1c69b4 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,17 +1,16 @@ addPsr4("SwoftTest\\Testing\\", $vendor . '/swoft/framework/test/testing/'); + +$application = new TestApplication([ + 'basePath' => $baseDir, +]); +$application->setBeanFile($baseDir . '/app/bean.php'); +$application->run(); diff --git a/test/testing/bean.php b/test/testing/bean.php deleted file mode 100644 index 0eb0174d..00000000 --- a/test/testing/bean.php +++ /dev/null @@ -1,7 +0,0 @@ - [ - 'path' => __DIR__ . '/../config', - ] -]; From a0f8b8aa4b9cc27397368e4e0c8f15ee919cb2fc Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 16 Jan 2020 10:15:14 +0800 Subject: [PATCH 589/643] Update label.yml --- .github/workflows/label.yml | 38 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 4a7e3a26..35bf54f4 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -25,47 +25,45 @@ jobs: assignees: '["sakuraovq"]' # labels to be set when keyword is found labels: '["swoft: db"]' # optional + - name: AOP Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + keywords: '["aop", "Point"]' + assignees: '["stelin"]' + labels: '["swoft: aop"]' + - name: Config Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + keywords: '["env", "config"]' + assignees: '["stelin", "inhere"]' + labels: '["swoft: config"]' - name: Event Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: - # Github token github-token: "${{ secrets.GITHUB_TOKEN }}" - # keywords to look for in the issue keywords: '["event", "trigger"]' - # assignees to be assigned when keyword is found assignees: '["inhere"]' - # labels to be set when keyword is found - labels: '["swoft: event"]' # optional + labels: '["swoft: event"]' - name: Http Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: - # Github token github-token: "${{ secrets.GITHUB_TOKEN }}" - # keywords to look for in the issue keywords: '["http", "http-server", "http-client", "http-message"]' - # assignees to be assigned when keyword is found assignees: '["stelin", "inhere"]' - # labels to be set when keyword is found - labels: '["swoft: http"]' # optional + labels: '["swoft: http"]' - name: Rpc Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: - # Github token github-token: "${{ secrets.GITHUB_TOKEN }}" - # keywords to look for in the issue keywords: '["rpc", "rpc-server", "rpc-client"]' - # assignees to be assigned when keyword is found assignees: '["stelin"]' - # labels to be set when keyword is found - labels: '["swoft: rpc"]' # optional + labels: '["swoft: rpc"]' - name: Websocket Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: - # Github token github-token: "${{ secrets.GITHUB_TOKEN }}" - # keywords to look for in the issue keywords: '["websocket", "websocket-server", "ws-server"]' - # assignees to be assigned when keyword is found assignees: '["inhere"]' - # labels to be set when keyword is found - labels: '["swoft: websocket"]' # optional + labels: '["swoft: websocket"]' From 2fe21a2fee259fc1cccd8a8094ec67e00c74aca5 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 16 Jan 2020 10:15:53 +0800 Subject: [PATCH 590/643] Update label.yml --- .github/workflows/label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 35bf54f4..6c549140 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -50,7 +50,7 @@ jobs: uses: Naturalclar/issue-action@v1.0.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - keywords: '["http", "http-server", "http-client", "http-message"]' + keywords: '["http-server", "http-client", "http-message"]' assignees: '["stelin", "inhere"]' labels: '["swoft: http"]' - name: Rpc Issue Labeler From c51406125a61ba9bd8f2d7fc6e4b40016c05987f Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 16 Jan 2020 20:18:07 +0800 Subject: [PATCH 591/643] Update .travis.yml --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bab6451d..88baa188 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - | echo "no" | pecl install -f redis - | - wget https://github.com/swoole/swoole-src/archive/v4.4.6.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole + wget https://github.com/swoole/swoole-src/archive/v4.4.15.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - | wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" @@ -22,7 +22,7 @@ install: before_script: - phpenv config-rm xdebug.ini - composer config -g process-timeout 900 && composer update - - composer require --dev phpstan/phpstan-shim + - composer require --dev phpstan/phpstan script: - composer cs-fix From 270033225c07a94874a59d9962d63eb2a881327a Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 16 Jan 2020 21:00:52 +0800 Subject: [PATCH 592/643] Update phpstan.neon.dist --- phpstan.neon.dist | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8760f4a9..203d0836 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -3,6 +3,7 @@ includes: parameters: level: max inferPrivatePropertyTypeFromConstructor: true + checkMissingIterableValueType: false paths: - %currentWorkingDirectory%/app/ autoload_files: From 2fa2bd5fb589a9a61413311c46fb6793db244c81 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 17 Jan 2020 19:58:54 +0800 Subject: [PATCH 593/643] update some --- test/apitest/.keep | 1 + test/run.php | 14 -------------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/test/apitest/.keep b/test/apitest/.keep index e69de29b..18e1ba37 100644 --- a/test/apitest/.keep +++ b/test/apitest/.keep @@ -0,0 +1 @@ +If you want run api test, must start an server before run tests. diff --git a/test/run.php b/test/run.php index 8069fece..9e2acf85 100755 --- a/test/run.php +++ b/test/run.php @@ -43,20 +43,6 @@ die(1); } -if (array_reverse(explode('/', __DIR__))[0] ?? '' === 'test') { - $vendor_dir = dirname(PHPUNIT_COMPOSER_INSTALL); - $bin_unit = "{$vendor_dir}/bin/phpunit"; - $unit_uint = "{$vendor_dir}/phpunit/phpunit/phpunit"; - if (file_exists($bin_unit)) { - @unlink($bin_unit); - @symlink(__FILE__, $bin_unit); - } - if (file_exists($unit_uint)) { - @unlink($unit_uint); - @symlink(__FILE__, $unit_uint); - } -} - if (!in_array('-c', $_SERVER['argv'], true)) { $_SERVER['argv'][] = '-c'; $_SERVER['argv'][] = dirname(__DIR__) . '/phpunit.xml'; From 610909223b915ab38b5ff249dde5878adb8d9f54 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 17 Jan 2020 20:51:40 +0800 Subject: [PATCH 594/643] update some for format check --- app/Http/Controller/CacheController.php | 2 +- app/bean.php | 8 +++++++- bin/bootstrap.php | 4 +--- phpstan.neon.dist | 2 +- test/bootstrap.php | 11 +++++++++-- test/run.php | 26 +++++++++++++++++++------ test/testing/AutoLoader.php | 8 ++++++++ test/unit/ExampleTest.php | 9 ++++++++- 8 files changed, 55 insertions(+), 15 deletions(-) diff --git a/app/Http/Controller/CacheController.php b/app/Http/Controller/CacheController.php index b39a4399..510aa33d 100644 --- a/app/Http/Controller/CacheController.php +++ b/app/Http/Controller/CacheController.php @@ -10,7 +10,7 @@ namespace App\Http\Controller; -use Psr\SimpleCache\InvalidArgumentException; +use InvalidArgumentException; use Swoft\Cache\Cache; use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; diff --git a/app/bean.php b/app/bean.php index e0f982d9..ec3be201 100644 --- a/app/bean.php +++ b/app/bean.php @@ -136,17 +136,23 @@ 'class' => WebSocketServer::class, 'port' => 18308, 'listener' => [ - // 'rpc' => bean('rpcServer'), + 'rpc' => bean('rpcServer'), // 'tcp' => bean('tcpServer'), ], 'on' => [ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), + // Enable task must add task and finish event + SwooleEvent::TASK => bean(TaskListener::class), + SwooleEvent::FINISH => bean(FinishListener::class) ], 'debug' => 1, // 'debug' => env('SWOFT_DEBUG', 0), /* @see WebSocketServer::$setting */ 'setting' => [ + 'task_worker_num' => 6, + 'task_enable_coroutine' => true, + 'worker_num' => 6, 'log_file' => alias('@runtime/swoole.log'), ], ], diff --git a/bin/bootstrap.php b/bin/bootstrap.php index bd8c79bb..e246bf47 100644 --- a/bin/bootstrap.php +++ b/bin/bootstrap.php @@ -11,6 +11,4 @@ /** @var \Composer\Autoload\ClassLoader $loader */ $loader = require dirname(__DIR__) . '/vendor/autoload.php'; -$loader->addPsr4("Swoft\\Cache\\", 'vendor/swoft/cache/src/'); -$loader->addPsr4("Swoft\\Swlib\\", 'vendor/swoft/swlib/src/'); -$loader->addPsr4("Swoft\\Serialize\\", 'vendor/swoft/serialize/src/'); +// $loader->addPsr4('Swoft\\Cache\\', 'vendor/swoft/cache/src/'); diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 8760f4a9..fe2bdc92 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -6,7 +6,7 @@ parameters: paths: - %currentWorkingDirectory%/app/ autoload_files: - - %currentWorkingDirectory%/test/bootstrap.php + - %currentWorkingDirectory%/bin/bootstrap.php autoload_directories: - %currentWorkingDirectory%/vendor/swoft/swoole-ide-helper/output/namespace/ dynamicConstantNames: diff --git a/test/bootstrap.php b/test/bootstrap.php index cd1c69b4..2090aff5 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -1,5 +1,12 @@ addPsr4("SwoftTest\\Testing\\", $vendor . '/swoft/framework/test/testing/'); +$loader->addPsr4('SwoftTest\\Testing\\', $vendor . '/swoft/framework/test/testing/'); $application = new TestApplication([ 'basePath' => $baseDir, diff --git a/test/run.php b/test/run.php index 9e2acf85..d1ec1e53 100755 --- a/test/run.php +++ b/test/run.php @@ -1,5 +1,12 @@ ')) { - fwrite(STDERR, - sprintf('This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, - PHP_VERSION, PHP_BINARY)); + fwrite( + STDERR, + sprintf( + 'This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, + PHP_VERSION, + PHP_BINARY + ) + ); die(1); } @@ -36,10 +48,12 @@ } if (!defined('PHPUNIT_COMPOSER_INSTALL')) { - fwrite(STDERR, + fwrite( + STDERR, "You need to set up the project dependencies using Composer:\n\n" . ' composer install' . PHP_EOL . PHP_EOL - . 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL); + . 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL + ); die(1); } diff --git a/test/testing/AutoLoader.php b/test/testing/AutoLoader.php index 40f2c74c..a0a5b1d2 100644 --- a/test/testing/AutoLoader.php +++ b/test/testing/AutoLoader.php @@ -1,4 +1,12 @@ Date: Fri, 17 Jan 2020 21:50:33 +0800 Subject: [PATCH 595/643] fix missing prams --- app/Validator/Rule/AlphaDashRule.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Validator/Rule/AlphaDashRule.php b/app/Validator/Rule/AlphaDashRule.php index 621303db..76d8cc3a 100644 --- a/app/Validator/Rule/AlphaDashRule.php +++ b/app/Validator/Rule/AlphaDashRule.php @@ -23,15 +23,16 @@ class AlphaDashRule implements RuleInterface { /** - * @param array $data + * @param array $data * @param string $propertyName * @param object $item - * @param null $default + * @param null $default + * @param bool $strict * * @return array * @throws ValidatorException */ - public function validate(array $data, string $propertyName, $item, $default = null): array + public function validate(array $data, string $propertyName, $item, $default = null, $strict = false): array { $message = $item->getMessage(); if (!isset($data[$propertyName]) && $default === null) { From 3023511b6361027ec809b57c7d5afb1cf45ec222 Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 18 Jan 2020 10:00:32 +0800 Subject: [PATCH 596/643] update some --- test/run.php | 34 +++++++++++++++------------------- test/unit/ExampleTest.php | 5 +++++ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/test/run.php b/test/run.php index d1ec1e53..057ec7a4 100755 --- a/test/run.php +++ b/test/run.php @@ -12,11 +12,6 @@ use Swoole\Coroutine; use Swoole\ExitException; -Coroutine::set([ - 'log_level' => SWOOLE_LOG_INFO, - 'trace_flags' => 0 -]); - /* * This file is part of PHPUnit. * @@ -26,14 +21,9 @@ * file that was distributed with this source code. */ if (version_compare('7.1.0', PHP_VERSION, '>')) { - fwrite( - STDERR, - sprintf( - 'This version of PHPUnit is supported on PHP 7.1 and PHP 7.2.' . PHP_EOL . 'You are using PHP %s (%s).' . PHP_EOL, - PHP_VERSION, - PHP_BINARY - ) - ); + $tips = "This version of PHPUnit is supported on PHP 7.1 and PHP 7.2. \nYou are using PHP %s (%s)."; + + fwrite(STDERR, sprintf($tips, PHP_VERSION, PHP_BINARY)); die(1); } @@ -48,12 +38,12 @@ } if (!defined('PHPUNIT_COMPOSER_INSTALL')) { - fwrite( - STDERR, - "You need to set up the project dependencies using Composer:\n\n" - . ' composer install' . PHP_EOL . PHP_EOL - . 'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL - ); + $tips = << SWOOLE_LOG_INFO, + 'trace_flags' => 0 +]); + \Swoft\Co::run(function () { // Status global $status; diff --git a/test/unit/ExampleTest.php b/test/unit/ExampleTest.php index b2b7abef..7de7125b 100644 --- a/test/unit/ExampleTest.php +++ b/test/unit/ExampleTest.php @@ -11,7 +11,12 @@ namespace AppTest\Unit; use PHPUnit\Framework\TestCase; +use function bean; class ExampleTest extends TestCase { + public function testDemo(): void + { + $this->assertNotEmpty(bean('cliApp')); + } } From a997a116925d59ba2dad3482130765bb20f6b484 Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 18 Jan 2020 11:05:28 +0800 Subject: [PATCH 597/643] update some --- phpstan.neon.dist | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index fe2bdc92..99143486 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -11,6 +11,11 @@ parameters: - %currentWorkingDirectory%/vendor/swoft/swoole-ide-helper/output/namespace/ dynamicConstantNames: - APP_DEBUG + - SWOFT_DEBUG + excludes_analyse: + - test/* + - runtime/* + - resource/* ignoreErrors: # Variable type - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' @@ -19,6 +24,8 @@ parameters: - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' # - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' - '#^Call to an undefined method\sPsr\\Http\\Message\\ServerRequestInterface::getUriPath\(\)#' + - '#.+onstant APP_DEBUG not found.#' + - '#^Call to static method \w+\(\) on an unknown class Swoft\\Cache\\Cache.#' # These are ignored for now - path: %currentWorkingDirectory%/app/Http/Controller/DbModelController.php From 04482b0454c106d3d199405d777a78edab2d8f2e Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 18 Jan 2020 17:55:34 +0800 Subject: [PATCH 598/643] Update label.yml --- .github/workflows/label.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 6c549140..c848525d 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -32,6 +32,13 @@ jobs: keywords: '["aop", "Point"]' assignees: '["stelin"]' labels: '["swoft: aop"]' + - name: Validate Issue Labeler + uses: Naturalclar/issue-action@v1.0.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + keywords: '["validator", "validate"]' + assignees: '["stelin", "JasonYH"]' + labels: '["swoft: validate"]' - name: Config Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: From c6cdd3972e02242c79d5b6b7adbc5355e6d4823a Mon Sep 17 00:00:00 2001 From: Inhere Date: Sat, 18 Jan 2020 17:56:22 +0800 Subject: [PATCH 599/643] Update label.yml --- .github/workflows/label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index c848525d..b7052a08 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -38,7 +38,7 @@ jobs: github-token: "${{ secrets.GITHUB_TOKEN }}" keywords: '["validator", "validate"]' assignees: '["stelin", "JasonYH"]' - labels: '["swoft: validate"]' + labels: '["swoft: validator"]' - name: Config Issue Labeler uses: Naturalclar/issue-action@v1.0.0 with: From 95389738f9032cc8e29e83f0a1e917eac1cc2275 Mon Sep 17 00:00:00 2001 From: inhere Date: Mon, 20 Jan 2020 13:23:01 +0800 Subject: [PATCH 600/643] up: add ws test page --- app/Http/Controller/HomeController.php | 11 + resource/views/home/ws-test.php | 394 +++++++++++++++++++++++++ 2 files changed, 405 insertions(+) create mode 100644 resource/views/home/ws-test.php diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 5e37a48e..88b8f53e 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -58,4 +58,15 @@ public function hello(string $name): Response { return context()->getResponse()->withContent('Hello' . ($name === '' ? '' : ", {$name}")); } + + /** + * @RequestMapping("/wstest", method={"GET"}) + * + * @return Response + * @throws Throwable + */ + public function wsTest(): Response + { + return view('home/ws-test'); + } } diff --git a/resource/views/home/ws-test.php b/resource/views/home/ws-test.php new file mode 100644 index 00000000..76fae6a7 --- /dev/null +++ b/resource/views/home/ws-test.php @@ -0,0 +1,394 @@ + + + + + + + WebSocket Testing - Swoft 2.0 + + + + + + +
          +
          +
          +
          + +
          +
          +
          +
          +
          Configuration
          +
          +
          WebSocket Server Address
          +
          + +
          + +
          + + +
          +
          +

          Please note that the server address, port, path, etc. are correct

          +
          +
          +
          + + +
          +
          + + +
          +
          +
          + +
          +
          + +
          +
          +
          Messages
          +
          +

          websocket message box

          +
          +
          +
          + You at 2020-1-20 10:54:40 +
          +
          +
          + User send message example +
          +
          +
          +
          +
          + Server at 2020-1-20 10:54:40 +
          +
          +
          + Server reply message example +
          +
          +
          +
          +
          + +
          +
          +
          +
          +
          +
          +
          + + + + + + + From 7b2145f74c77366b61c5f300ffa32e765ae6ae73 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 21 Jan 2020 20:28:04 +0800 Subject: [PATCH 601/643] Update Dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b8b0f967..4280aa04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=5.1.0 \ - SWOOLE_VERSION=4.4.14 \ + SWOOLE_VERSION=4.4.15 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From 3678b3e8e234ddb5bae8a616abcf9f2178672075 Mon Sep 17 00:00:00 2001 From: doobas Date: Tue, 11 Feb 2020 08:40:35 +0200 Subject: [PATCH 602/643] English documentation url English documentation link does't work. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca1b0e57..47260172 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw ## Document - [中文文档](https://www.swoft.org/docs) -- [English](https://en.swoft.org/docs) +- [English](http://swoft.io/docs) ## Discuss From c869ad640b07228791c9e13b2a0aa5dfff6cea27 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 25 Mar 2020 12:30:02 +0800 Subject: [PATCH 603/643] upgrade swoole to 4.4.16 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 4280aa04..075b1657 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=5.1.0 \ - SWOOLE_VERSION=4.4.15 \ + SWOOLE_VERSION=4.4.16 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From 0ab3dc5395a4be3265b594ed0e40bb807d262e2d Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 2 Apr 2020 20:01:17 +0800 Subject: [PATCH 604/643] upgrade swoole to 4.4.17 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 075b1657..11cc8c6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"prod"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=5.1.0 \ - SWOOLE_VERSION=4.4.16 \ + SWOOLE_VERSION=4.4.17 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From bcb8533aeed42d3359efb2fe0ea3fe05620e0ad3 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 12 Apr 2020 14:35:28 +0800 Subject: [PATCH 605/643] add more examples --- app/Aspect/BeanAspect.php | 35 ++++++++++++ app/Aspect/RequestBeanAspect.php | 54 +++++++++++++++++++ app/Common/MyBean.php | 22 ++++++++ app/Common/RpcProvider.php | 4 -- app/Http/Controller/BeanController.php | 22 ++++++-- app/Http/Controller/HomeController.php | 12 +++++ app/Http/Controller/SelectDbController.php | 6 --- app/Model/Data/GoodsData.php | 19 +++++++ app/Model/Logic/ConsulLogic.php | 4 -- app/Model/Logic/RequestBean.php | 2 + app/WebSocket/Chat/HomeController.php | 13 +++++ app/WebSocket/ChatModule.php | 23 ++++++++ .../Middleware/GlobalWsMiddleware.php | 2 + app/bean.php | 6 ++- config/app.php | 9 ++++ database/Migration/Count.php | 6 --- database/Migration/Desc.php | 6 --- 17 files changed, 214 insertions(+), 31 deletions(-) create mode 100644 app/Aspect/BeanAspect.php create mode 100644 app/Aspect/RequestBeanAspect.php create mode 100644 app/Common/MyBean.php create mode 100644 app/Model/Data/GoodsData.php create mode 100644 config/app.php diff --git a/app/Aspect/BeanAspect.php b/app/Aspect/BeanAspect.php new file mode 100644 index 00000000..bffadecf --- /dev/null +++ b/app/Aspect/BeanAspect.php @@ -0,0 +1,35 @@ +temp); + } + + /** + * @After() + */ + public function afterRun(): void + { + $id = (string)Co::tid(); + /** @var RequestBean $rb */ + $rb = BF::getRequestBean('requestBean', $id); + + vdump(__METHOD__, $rb->temp); + } +} diff --git a/app/Common/MyBean.php b/app/Common/MyBean.php new file mode 100644 index 00000000..a6cd49b7 --- /dev/null +++ b/app/Common/MyBean.php @@ -0,0 +1,22 @@ +myMethod()]; + } + + /** + * @RequestMapping("req") * * @return array */ @@ -37,15 +50,18 @@ public function request(): array /** @var RequestBean $request */ $request = BeanFactory::getRequestBean('requestBean', $id); + + $request->temp = ['rid' => $id]; + return $request->getData(); } /** * @return array * - * @RequestMapping() + * @RequestMapping("req2") */ - public function requestClass(): array + public function requestTwo(): array { $id = (string)Co::tid(); diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 88b8f53e..0779bda1 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -10,6 +10,7 @@ namespace App\Http\Controller; +use App\Model\Data\GoodsData; use Swoft; use Swoft\Http\Message\ContentType; use Swoft\Http\Message\Response; @@ -69,4 +70,15 @@ public function wsTest(): Response { return view('home/ws-test'); } + + /** + * @RequestMapping("/dataConfig", method={"GET"}) + * + * @return array + * @throws Throwable + */ + public function dataConfig(): array + { + return \bean(GoodsData::class)->getConfig(); + } } diff --git a/app/Http/Controller/SelectDbController.php b/app/Http/Controller/SelectDbController.php index be4eed2c..245cc96b 100644 --- a/app/Http/Controller/SelectDbController.php +++ b/app/Http/Controller/SelectDbController.php @@ -14,8 +14,6 @@ use App\Model\Entity\Desc; use App\Model\Entity\User; use Exception; -use ReflectionException; -use Swoft\Bean\Exception\ContainerException; use Swoft\Db\DB; use Swoft\Db\Exception\DbException; use Swoft\Http\Server\Annotation\Mapping\Controller; @@ -217,9 +215,7 @@ public function select(): array /** * @return bool - * @throws ContainerException * @throws DbException - * @throws ReflectionException */ public function insertId2(): bool { @@ -237,8 +233,6 @@ public function insertId2(): bool } /** - * @throws ReflectionException - * @throws ContainerException * @throws DbException */ public function desc(): array diff --git a/app/Model/Data/GoodsData.php b/app/Model/Data/GoodsData.php new file mode 100644 index 00000000..7ada0517 --- /dev/null +++ b/app/Model/Data/GoodsData.php @@ -0,0 +1,19 @@ +push($request->getFd(), "Opened, welcome!(FD: $fd)"); + + $fullClass = Session::current()->getParserClass(); + $className = basename($fullClass); + + $help = << App\WebSocket\Chat\HomeController::index() +TXT; + + server()->push($fd, $help); } } diff --git a/app/WebSocket/Middleware/GlobalWsMiddleware.php b/app/WebSocket/Middleware/GlobalWsMiddleware.php index c65ed5c9..662d4127 100644 --- a/app/WebSocket/Middleware/GlobalWsMiddleware.php +++ b/app/WebSocket/Middleware/GlobalWsMiddleware.php @@ -42,6 +42,8 @@ public function process(RequestInterface $request, MessageHandlerInterface $hand CLog::info('after handle'); + \server()->log(__METHOD__, [], 'error'); + return $resp; } } diff --git a/app/bean.php b/app/bean.php index ec3be201..293ba200 100644 --- a/app/bean.php +++ b/app/bean.php @@ -42,7 +42,6 @@ 'listener' => [ // 'rpc' => bean('rpcServer'), // 'tcp' => bean('tcpServer'), - // 'ws' => bean('wsServer') ], 'process' => [ // 'monitor' => bean(MonitorProcess::class) @@ -57,7 +56,10 @@ 'setting' => [ 'task_worker_num' => 12, 'task_enable_coroutine' => true, - 'worker_num' => 6 + 'worker_num' => 6, + // static handle + // 'enable_static_handler' => true, + // 'document_root' => dirname(__DIR__) . '/public', ] ], 'httpDispatcher' => [ diff --git a/config/app.php b/config/app.php new file mode 100644 index 00000000..36bb9e1c --- /dev/null +++ b/config/app.php @@ -0,0 +1,9 @@ + ['a', 'b'], + 'warehouseCode' => $envVal ? explode(',', $envVal) : [], +]; diff --git a/database/Migration/Count.php b/database/Migration/Count.php index 663def29..c126230f 100644 --- a/database/Migration/Count.php +++ b/database/Migration/Count.php @@ -10,8 +10,6 @@ namespace Database\Migration; -use ReflectionException; -use Swoft\Bean\Exception\ContainerException; use Swoft\Db\Exception\DbException; use Swoft\Db\Schema\Blueprint; use Swoft\Devtool\Annotation\Mapping\Migration; @@ -27,9 +25,7 @@ class Count extends BaseMigration { /** - * @throws ContainerException * @throws DbException - * @throws ReflectionException */ public function up(): void { @@ -49,8 +45,6 @@ public function up(): void } /** - * @throws ReflectionException - * @throws ContainerException * @throws DbException */ public function down(): void diff --git a/database/Migration/Desc.php b/database/Migration/Desc.php index ee215ff0..b9147b3e 100644 --- a/database/Migration/Desc.php +++ b/database/Migration/Desc.php @@ -10,8 +10,6 @@ namespace Database\Migration; -use ReflectionException; -use Swoft\Bean\Exception\ContainerException; use Swoft\Db\Exception\DbException; use Swoft\Db\Schema\Blueprint; use Swoft\Devtool\Annotation\Mapping\Migration; @@ -27,9 +25,7 @@ class Desc extends BaseMigration { /** - * @throws ContainerException * @throws DbException - * @throws ReflectionException */ public function up(): void { @@ -42,9 +38,7 @@ public function up(): void } /** - * @throws ContainerException * @throws DbException - * @throws ReflectionException */ public function down(): void { From b420435ab87b6232dc2e0beee6672ed14470b06b Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sun, 12 Apr 2020 15:44:41 +0800 Subject: [PATCH 606/643] change ab test curreccy --- app/Console/Command/TestCommand.php | 4 ++-- app/Http/Controller/RedisController.php | 8 +------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/app/Console/Command/TestCommand.php b/app/Console/Command/TestCommand.php index c82cabae..71bffbbd 100644 --- a/app/Console/Command/TestCommand.php +++ b/app/Console/Command/TestCommand.php @@ -49,7 +49,7 @@ public function ab(): void foreach ($exeUris as $uri) { $curlResult = null; - $abShell = sprintf('ab -k -n 10000 -c 2000 127.0.0.1:18306%s', $uri); + $abShell = sprintf('ab -k -n 10000 -c 500 127.0.0.1:18306%s', $uri); $curlShell = sprintf('curl 127.0.0.1:18306%s', $uri); exec($curlShell, $curlResult); @@ -114,7 +114,7 @@ private function uris(): array '/rpc/getList', '/rpc/returnBool', '/rpc/bigString', -// '/rpc/sendBigString', + '/rpc/sendBigString', '/rpc/returnNull' ], 'co' => [ diff --git a/app/Http/Controller/RedisController.php b/app/Http/Controller/RedisController.php index 3bd81df6..8c916a6e 100644 --- a/app/Http/Controller/RedisController.php +++ b/app/Http/Controller/RedisController.php @@ -46,13 +46,7 @@ public function poolSet(): array $get = $this->redis->get($key); - $isError = $this->redis->call(function (\Redis $redis) { - $redis->eval('returnxxxx 1'); - - return $redis->getLastError(); - }); - - return [$get, $value, $isError]; + return [$get, $value]; } /** From 5b18fe32ba77e6a0915086c63d5205cf482efbcb Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 14 Apr 2020 11:40:11 +0800 Subject: [PATCH 607/643] add an bean config for ws server --- app/bean.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/bean.php b/app/bean.php index 293ba200..1ad78ca8 100644 --- a/app/bean.php +++ b/app/bean.php @@ -156,6 +156,7 @@ 'task_enable_coroutine' => true, 'worker_num' => 6, 'log_file' => alias('@runtime/swoole.log'), + // 'open_websocket_close_frame' => true, ], ], // 'wsConnectionManager' => [ From 072b25d3ed9d4d0aad4c9145e4ff27b53a784f4c Mon Sep 17 00:00:00 2001 From: inhere Date: Tue, 14 Apr 2020 11:43:49 +0800 Subject: [PATCH 608/643] fix style error --- app/Common/MyBean.php | 8 ++++++++ app/Model/Data/GoodsData.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/app/Common/MyBean.php b/app/Common/MyBean.php index a6cd49b7..0a1c0e10 100644 --- a/app/Common/MyBean.php +++ b/app/Common/MyBean.php @@ -1,4 +1,12 @@ Date: Tue, 14 Apr 2020 14:38:58 +0800 Subject: [PATCH 609/643] fix phpstan error --- .travis.yml | 4 ++-- app/Http/Controller/HomeController.php | 3 ++- app/Model/Logic/RequestBean.php | 3 +++ composer.cn.json | 3 ++- composer.json | 4 ++-- phpstan.neon.dist | 6 ++++-- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 88baa188..e8b32cba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - | echo "no" | pecl install -f redis - | - wget https://github.com/swoole/swoole-src/archive/v4.4.15.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole + wget https://github.com/swoole/swoole-src/archive/v4.4.17.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - | wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" @@ -25,6 +25,6 @@ before_script: - composer require --dev phpstan/phpstan script: - - composer cs-fix + - composer check-cs - composer test # - php bin/swoft dinfo:env diff --git a/app/Http/Controller/HomeController.php b/app/Http/Controller/HomeController.php index 0779bda1..8c71b787 100644 --- a/app/Http/Controller/HomeController.php +++ b/app/Http/Controller/HomeController.php @@ -18,6 +18,7 @@ use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\View\Renderer; use Throwable; +use function bean; use function context; /** @@ -79,6 +80,6 @@ public function wsTest(): Response */ public function dataConfig(): array { - return \bean(GoodsData::class)->getConfig(); + return bean(GoodsData::class)->getConfig(); } } diff --git a/app/Model/Logic/RequestBean.php b/app/Model/Logic/RequestBean.php index 47ec47f6..4f9217f9 100644 --- a/app/Model/Logic/RequestBean.php +++ b/app/Model/Logic/RequestBean.php @@ -21,6 +21,9 @@ */ class RequestBean { + /** + * @var array + */ public $temp = []; /** diff --git a/composer.cn.json b/composer.cn.json index 97a8c40e..a63797b5 100644 --- a/composer.cn.json +++ b/composer.cn.json @@ -49,7 +49,8 @@ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], "test": "./vendor/bin/phpunit -c phpunit.xml", - "cs-fix": "./vendor/bin/php-cs-fixer fix $1" + "check-cs": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff", + "cs-fix": "./bin/php-cs-fixer fix" }, "repositories": { "packagist": { diff --git a/composer.json b/composer.json index 7a76f90a..a1ef3cb0 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ "./vendor/bin/phpstan analyze", "./vendor/bin/phpunit -c phpunit.xml" ], - "cs-fix": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff", - "do-cs-fix": "./bin/php-cs-fixer fix" + "check-cs": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff", + "cs-fix": "./bin/php-cs-fixer fix" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c89ddc84..57eb2f2a 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -10,6 +10,7 @@ parameters: - %currentWorkingDirectory%/bin/bootstrap.php autoload_directories: - %currentWorkingDirectory%/vendor/swoft/swoole-ide-helper/output/namespace/ + - %currentWorkingDirectory%/vendor/swoft/ dynamicConstantNames: - APP_DEBUG - SWOFT_DEBUG @@ -20,13 +21,14 @@ parameters: ignoreErrors: # Variable type - '#^Call to an undefined method Swoft\\Contract\\ContextInterface::get\S+\(\)\.$#' - - '#^Call to an undefined method Swoft\\Contract\\SessionInterface::push\(\)#' + - '#^Call to an undefined method Swoft\\Contract\\SessionInterface::\S+\(\)#' # - '#^Call to an undefined method Swoft\\Session\\SessionInterface::getFd\(\)#' - '#^Call to an undefined method Swoft\\Server\\Server::push\(\)\.$#' # - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' - '#^Call to an undefined method\sPsr\\Http\\Message\\ServerRequestInterface::getUriPath\(\)#' - '#.+onstant APP_DEBUG not found.#' - - '#^Call to static method \w+\(\) on an unknown class Swoft\\Cache\\Cache.#' + - '#^Function view not found.#' + # - '#^Call to static method \w+\(\) on an unknown class Swoft\\Cache\\Cache.#' # These are ignored for now - path: %currentWorkingDirectory%/app/Http/Controller/DbModelController.php From 34030340a17abdba0259fbf69c05e79127862a0d Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 15 Apr 2020 09:48:12 +0800 Subject: [PATCH 610/643] format codes --- app/Annotation/Parser/AlphaDashParser.php | 4 +- app/Aspect/BeanAspect.php | 2 +- app/Exception/ApiException.php | 1 + app/Http/Controller/CacheController.php | 3 + app/Http/Controller/SessionController.php | 2 +- app/Http/Controller/TimerController.php | 2 +- app/Listener/RegisterServiceListener.php | 5 +- app/Model/Logic/MonitorLogic.php | 4 +- app/Model/Logic/RequestBean.php | 2 +- app/Process/Worker2Process.php | 6 +- app/Rpc/Lib/UserInterface.php | 2 +- app/Rpc/Service/UserService.php | 8 +- app/Rpc/Service/UserServiceV2.php | 1 + app/Task/Listener/FinishListener.php | 2 +- app/Tcp/Middleware/DemoMiddleware.php | 2 +- app/Tcp/Middleware/GlobalTcpMiddleware.php | 2 +- app/Validator/CustomerValidator.php | 2 +- app/WebSocket/EchoModule.php | 1 - app/WebSocket/Test/TestController.php | 1 + app/bean.php | 92 +++++++++++----------- 20 files changed, 72 insertions(+), 72 deletions(-) diff --git a/app/Annotation/Parser/AlphaDashParser.php b/app/Annotation/Parser/AlphaDashParser.php index a4d91280..fbd4d1a7 100644 --- a/app/Annotation/Parser/AlphaDashParser.php +++ b/app/Annotation/Parser/AlphaDashParser.php @@ -10,10 +10,10 @@ namespace App\Annotation\Parser; +use App\Annotation\Mapping\AlphaDash; use ReflectionException; use Swoft\Annotation\Annotation\Mapping\AnnotationParser; use Swoft\Annotation\Annotation\Parser\Parser; -use App\Annotation\Mapping\AlphaDash; use Swoft\Validator\Exception\ValidatorException; use Swoft\Validator\ValidatorRegister; @@ -25,7 +25,7 @@ class AlphaDashParser extends Parser { /** - * @param int $type + * @param int $type * @param object $annotationObject * * @return array diff --git a/app/Aspect/BeanAspect.php b/app/Aspect/BeanAspect.php index bffadecf..69ec0120 100644 --- a/app/Aspect/BeanAspect.php +++ b/app/Aspect/BeanAspect.php @@ -10,10 +10,10 @@ namespace App\Aspect; +use App\Common\MyBean; use Swoft\Aop\Annotation\Mapping\Aspect; use Swoft\Aop\Annotation\Mapping\Before; use Swoft\Aop\Annotation\Mapping\PointBean; -use App\Common\MyBean; use function vdump; /** diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php index e995d7b9..db96cf5f 100644 --- a/app/Exception/ApiException.php +++ b/app/Exception/ApiException.php @@ -7,6 +7,7 @@ * @contact group@swoft.org * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ + namespace App\Exception; /** diff --git a/app/Http/Controller/CacheController.php b/app/Http/Controller/CacheController.php index 510aa33d..31e571ff 100644 --- a/app/Http/Controller/CacheController.php +++ b/app/Http/Controller/CacheController.php @@ -30,6 +30,7 @@ class CacheController * * @return array * @throws InvalidArgumentException + * @throws \Psr\SimpleCache\InvalidArgumentException */ public function set(): array { @@ -44,6 +45,7 @@ public function set(): array * * @return array * @throws InvalidArgumentException + * @throws \Psr\SimpleCache\InvalidArgumentException */ public function get(): array { @@ -60,6 +62,7 @@ public function get(): array * * @return array * @throws InvalidArgumentException + * @throws \Psr\SimpleCache\InvalidArgumentException */ public function del(): array { diff --git a/app/Http/Controller/SessionController.php b/app/Http/Controller/SessionController.php index 4feab237..bd38c278 100644 --- a/app/Http/Controller/SessionController.php +++ b/app/Http/Controller/SessionController.php @@ -89,7 +89,7 @@ public function get(): array public function del(): array { $sess = HttpSession::current(); - $ok = $sess->delete('testKey'); + $ok = $sess->delete('testKey'); return ['delete' => $ok]; } diff --git a/app/Http/Controller/TimerController.php b/app/Http/Controller/TimerController.php index 881d26a6..260099c1 100644 --- a/app/Http/Controller/TimerController.php +++ b/app/Http/Controller/TimerController.php @@ -12,13 +12,13 @@ use App\Model\Entity\User; use Exception; -use function random_int; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; use Swoft\Log\Helper\Log; use Swoft\Redis\Redis; use Swoft\Stdlib\Helper\JsonHelper; use Swoft\Timer; +use function random_int; /** * Class TimerController diff --git a/app/Listener/RegisterServiceListener.php b/app/Listener/RegisterServiceListener.php index fdf7a71a..61d0d14a 100644 --- a/app/Listener/RegisterServiceListener.php +++ b/app/Listener/RegisterServiceListener.php @@ -16,7 +16,6 @@ use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; use Swoft\Http\Server\HttpServer; -use Swoft\Log\Helper\CLog; use Swoft\Server\SwooleEvent; /** @@ -63,7 +62,7 @@ public function handle(EventInterface $event): void // Register -// $this->agent->registerService($service); -// CLog::info('Swoft http register service success by consul!'); + // $this->agent->registerService($service); + // CLog::info('Swoft http register service success by consul!'); } } diff --git a/app/Model/Logic/MonitorLogic.php b/app/Model/Logic/MonitorLogic.php index a265473f..56be76d8 100644 --- a/app/Model/Logic/MonitorLogic.php +++ b/app/Model/Logic/MonitorLogic.php @@ -42,11 +42,11 @@ public function monitor(Process $process): void // Database $user = User::find(1)->toArray(); - CLog::info('user='.json_encode($user)); + CLog::info('user=' . json_encode($user)); // Redis Redis::set('test', 'ok'); - CLog::info('test='.Redis::get('test')); + CLog::info('test=' . Redis::get('test')); Coroutine::sleep(3); } diff --git a/app/Model/Logic/RequestBean.php b/app/Model/Logic/RequestBean.php index 4f9217f9..3f573570 100644 --- a/app/Model/Logic/RequestBean.php +++ b/app/Model/Logic/RequestBean.php @@ -39,7 +39,7 @@ public function getData(): array * * @return string */ - public function getName(string $type):string + public function getName(string $type): string { return 'name'; } diff --git a/app/Process/Worker2Process.php b/app/Process/Worker2Process.php index 78aa45e5..68708222 100644 --- a/app/Process/Worker2Process.php +++ b/app/Process/Worker2Process.php @@ -40,13 +40,13 @@ public function run(Pool $pool, int $workerId): void // Database $user = User::find(1)->toArray(); - CLog::info('user='.json_encode($user)); + CLog::info('user=' . json_encode($user)); // Redis Redis::set('test', 'ok'); - CLog::info('test='.Redis::get('test')); + CLog::info('test=' . Redis::get('test')); - CLog::info('worker-' . $workerId.' context='.context()->getWorkerId()); + CLog::info('worker-' . $workerId . ' context=' . context()->getWorkerId()); Coroutine::sleep(3); } diff --git a/app/Rpc/Lib/UserInterface.php b/app/Rpc/Lib/UserInterface.php index f4a27948..7b4d4db0 100644 --- a/app/Rpc/Lib/UserInterface.php +++ b/app/Rpc/Lib/UserInterface.php @@ -41,7 +41,7 @@ public function getBigContent(): string; /** * @return void */ - public function returnNull():void ; + public function returnNull(): void; /** * Exception diff --git a/app/Rpc/Service/UserService.php b/app/Rpc/Service/UserService.php index 10123836..2c98f2f1 100644 --- a/app/Rpc/Service/UserService.php +++ b/app/Rpc/Service/UserService.php @@ -12,6 +12,7 @@ use App\Rpc\Lib\UserInterface; use Exception; +use RuntimeException; use Swoft\Co; use Swoft\Rpc\Server\Annotation\Mapping\Service; @@ -51,7 +52,6 @@ public function delete(int $id): bool */ public function returnNull(): void { - return; } /** @@ -59,17 +59,17 @@ public function returnNull(): void */ public function getBigContent(): string { - $content = Co::readFile(__DIR__ . '/big.data'); - return $content; + return Co::readFile(__DIR__ . '/big.data'); } /** * Exception + * * @throws Exception */ public function exception(): void { - throw new Exception('exception version'); + throw new RuntimeException('exception version'); } /** diff --git a/app/Rpc/Service/UserServiceV2.php b/app/Rpc/Service/UserServiceV2.php index 561db6c1..3b068d61 100644 --- a/app/Rpc/Service/UserServiceV2.php +++ b/app/Rpc/Service/UserServiceV2.php @@ -68,6 +68,7 @@ public function getBigContent(): string /** * Exception + * * @throws Exception */ public function exception(): void diff --git a/app/Task/Listener/FinishListener.php b/app/Task/Listener/FinishListener.php index 40fbb356..2e9a8ff6 100644 --- a/app/Task/Listener/FinishListener.php +++ b/app/Task/Listener/FinishListener.php @@ -10,12 +10,12 @@ namespace App\Task\Listener; -use function context; use Swoft\Event\Annotation\Mapping\Listener; use Swoft\Event\EventHandlerInterface; use Swoft\Event\EventInterface; use Swoft\Log\Helper\CLog; use Swoft\Task\TaskEvent; +use function context; /** * Class FinishListener diff --git a/app/Tcp/Middleware/DemoMiddleware.php b/app/Tcp/Middleware/DemoMiddleware.php index df76cba3..8e1ff42c 100644 --- a/app/Tcp/Middleware/DemoMiddleware.php +++ b/app/Tcp/Middleware/DemoMiddleware.php @@ -12,8 +12,8 @@ use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Log\Helper\CLog; -use Swoft\Tcp\Server\Contract\RequestHandlerInterface; use Swoft\Tcp\Server\Contract\MiddlewareInterface; +use Swoft\Tcp\Server\Contract\RequestHandlerInterface; use Swoft\Tcp\Server\Contract\RequestInterface; use Swoft\Tcp\Server\Contract\ResponseInterface; diff --git a/app/Tcp/Middleware/GlobalTcpMiddleware.php b/app/Tcp/Middleware/GlobalTcpMiddleware.php index 06df847b..8823e250 100644 --- a/app/Tcp/Middleware/GlobalTcpMiddleware.php +++ b/app/Tcp/Middleware/GlobalTcpMiddleware.php @@ -12,8 +12,8 @@ use Swoft\Bean\Annotation\Mapping\Bean; use Swoft\Log\Helper\CLog; -use Swoft\Tcp\Server\Contract\RequestHandlerInterface; use Swoft\Tcp\Server\Contract\MiddlewareInterface; +use Swoft\Tcp\Server\Contract\RequestHandlerInterface; use Swoft\Tcp\Server\Contract\RequestInterface; use Swoft\Tcp\Server\Contract\ResponseInterface; diff --git a/app/Validator/CustomerValidator.php b/app/Validator/CustomerValidator.php index 576e2cf4..fd30a923 100644 --- a/app/Validator/CustomerValidator.php +++ b/app/Validator/CustomerValidator.php @@ -33,7 +33,7 @@ class CustomerValidator implements ValidatorInterface public function validate(array $data, array $params): array { $start = $data['start'] ?? null; - $end = $data['end'] ?? null; + $end = $data['end'] ?? null; if ($start === null && $end === null) { throw new ValidatorException('Start time and end time cannot be empty'); } diff --git a/app/WebSocket/EchoModule.php b/app/WebSocket/EchoModule.php index 1c97cf0b..834102be 100644 --- a/app/WebSocket/EchoModule.php +++ b/app/WebSocket/EchoModule.php @@ -17,7 +17,6 @@ use Swoft\WebSocket\Server\Annotation\Mapping\WsModule; use Swoole\WebSocket\Frame; use Swoole\WebSocket\Server; -use function server; /** * Class EchoModule diff --git a/app/WebSocket/Test/TestController.php b/app/WebSocket/Test/TestController.php index fd7d7b6f..734c279f 100644 --- a/app/WebSocket/Test/TestController.php +++ b/app/WebSocket/Test/TestController.php @@ -42,6 +42,7 @@ public function index(): void /** * Message command is: 'test.index' + * * @param Message $msg * * @return void diff --git a/app/bean.php b/app/bean.php index 1ad78ca8..76563886 100644 --- a/app/bean.php +++ b/app/bean.php @@ -7,22 +7,19 @@ * @contact group@swoft.org * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ -use App\Common\DbSelector; -use App\Process\MonitorProcess; -use Swoft\Crontab\Process\CrontabProcess; + +use Swoft\Db\Database; use Swoft\Db\Pool; use Swoft\Http\Server\HttpServer; -use Swoft\Task\Swoole\SyncTaskListener; -use Swoft\Task\Swoole\TaskListener; -use Swoft\Task\Swoole\FinishListener; +use Swoft\Http\Server\Swoole\RequestListener; +use Swoft\Redis\RedisDb; use Swoft\Rpc\Client\Client as ServiceClient; use Swoft\Rpc\Client\Pool as ServicePool; use Swoft\Rpc\Server\ServiceServer; -use Swoft\Http\Server\Swoole\RequestListener; -use Swoft\WebSocket\Server\WebSocketServer; use Swoft\Server\SwooleEvent; -use Swoft\Db\Database; -use Swoft\Redis\RedisDb; +use Swoft\Task\Swoole\FinishListener; +use Swoft\Task\Swoole\TaskListener; +use Swoft\WebSocket\Server\WebSocketServer; return [ 'noticeHandler' => [ @@ -31,12 +28,12 @@ 'applicationHandler' => [ 'logFile' => '@runtime/logs/error-%d{Y-m-d}.log', ], - 'logger' => [ + 'logger' => [ 'flushRequest' => false, 'enable' => false, 'json' => false, ], - 'httpServer' => [ + 'httpServer' => [ 'class' => HttpServer::class, 'port' => 18306, 'listener' => [ @@ -44,16 +41,16 @@ // 'tcp' => bean('tcpServer'), ], 'process' => [ -// 'monitor' => bean(MonitorProcess::class) -// 'crontab' => bean(CrontabProcess::class) + // 'monitor' => bean(MonitorProcess::class) + // 'crontab' => bean(CrontabProcess::class) ], 'on' => [ -// SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task + // SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event SwooleEvent::FINISH => bean(FinishListener::class) ], /* @see HttpServer::$setting */ - 'setting' => [ + 'setting' => [ 'task_worker_num' => 12, 'task_enable_coroutine' => true, 'worker_num' => 6, @@ -62,7 +59,7 @@ // 'document_root' => dirname(__DIR__) . '/public', ] ], - 'httpDispatcher' => [ + 'httpDispatcher' => [ // Add global http middleware 'middlewares' => [ \App\Http\Middleware\FavIconMiddleware::class, @@ -75,38 +72,38 @@ \Swoft\Http\Server\Middleware\ValidatorMiddleware::class ] ], - 'db' => [ + 'db' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', - 'charset' => 'utf8mb4', + 'charset' => 'utf8mb4', ], - 'db2' => [ - 'class' => Database::class, - 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', - 'username' => 'root', - 'password' => 'swoft123456', -// 'dbSelector' => bean(DbSelector::class) + 'db2' => [ + 'class' => Database::class, + 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', + 'username' => 'root', + 'password' => 'swoft123456', + // 'dbSelector' => bean(DbSelector::class) ], - 'db2.pool' => [ + 'db2.pool' => [ 'class' => Pool::class, 'database' => bean('db2'), ], - 'db3' => [ + 'db3' => [ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456' ], - 'db3.pool' => [ + 'db3.pool' => [ 'class' => Pool::class, 'database' => bean('db3') ], - 'migrationManager' => [ + 'migrationManager' => [ 'migrationPath' => '@database/Migration', ], - 'redis' => [ + 'redis' => [ 'class' => RedisDb::class, 'host' => '127.0.0.1', 'port' => 6379, @@ -115,7 +112,7 @@ 'prefix' => 'swoft:' ] ], - 'user' => [ + 'user' => [ 'class' => ServiceClient::class, 'host' => '127.0.0.1', 'port' => 18307, @@ -127,35 +124,35 @@ ], 'packet' => bean('rpcClientPacket') ], - 'user.pool' => [ + 'user.pool' => [ 'class' => ServicePool::class, 'client' => bean('user'), ], - 'rpcServer' => [ + 'rpcServer' => [ 'class' => ServiceServer::class, ], - 'wsServer' => [ - 'class' => WebSocketServer::class, - 'port' => 18308, + 'wsServer' => [ + 'class' => WebSocketServer::class, + 'port' => 18308, 'listener' => [ 'rpc' => bean('rpcServer'), // 'tcp' => bean('tcpServer'), ], - 'on' => [ + 'on' => [ // Enable http handle SwooleEvent::REQUEST => bean(RequestListener::class), // Enable task must add task and finish event - SwooleEvent::TASK => bean(TaskListener::class), - SwooleEvent::FINISH => bean(FinishListener::class) + SwooleEvent::TASK => bean(TaskListener::class), + SwooleEvent::FINISH => bean(FinishListener::class) ], - 'debug' => 1, + 'debug' => 1, // 'debug' => env('SWOFT_DEBUG', 0), /* @see WebSocketServer::$setting */ - 'setting' => [ + 'setting' => [ 'task_worker_num' => 6, 'task_enable_coroutine' => true, 'worker_num' => 6, - 'log_file' => alias('@runtime/swoole.log'), + 'log_file' => alias('@runtime/swoole.log'), // 'open_websocket_close_frame' => true, ], ], @@ -166,29 +163,28 @@ // 'class' => \Swoft\Session\SwooleStorage::class, // ], /** @see \Swoft\WebSocket\Server\WsMessageDispatcher */ - 'wsMsgDispatcher' => [ + 'wsMsgDispatcher' => [ 'middlewares' => [ \App\WebSocket\Middleware\GlobalWsMiddleware::class ], ], /** @see \Swoft\Tcp\Server\TcpServer */ - 'tcpServer' => [ + 'tcpServer' => [ 'port' => 18309, 'debug' => 1, ], /** @see \Swoft\Tcp\Protocol */ - 'tcpServerProtocol' => [ + 'tcpServerProtocol' => [ // 'type' => \Swoft\Tcp\Packer\JsonPacker::TYPE, 'type' => \Swoft\Tcp\Packer\SimpleTokenPacker::TYPE, // 'openLengthCheck' => true, ], /** @see \Swoft\Tcp\Server\TcpDispatcher */ - 'tcpDispatcher' => [ + 'tcpDispatcher' => [ 'middlewares' => [ \App\Tcp\Middleware\GlobalTcpMiddleware::class ], ], - 'cliRouter' => [ - // 'disabledGroups' => ['demo', 'test'], + 'cliRouter' => [// 'disabledGroups' => ['demo', 'test'], ], ]; From eb18fe14a914bc3e6ac8eb174de9a315cb0ad36d Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 15 Apr 2020 10:55:02 +0800 Subject: [PATCH 611/643] add some phpcs setting, fix cs error --- .php_cs | 7 +++++-- app/Exception/ApiException.php | 6 ++++-- app/Helper/Functions.php | 2 +- app/bean.php | 2 +- bin/bootstrap.php | 2 +- test/bootstrap.php | 3 ++- test/run.php | 2 +- 7 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.php_cs b/.php_cs index 9c552f9e..e644064e 100644 --- a/.php_cs +++ b/.php_cs @@ -14,17 +14,20 @@ return PhpCsFixer\Config::create() ->setRules([ '@PSR2' => true, 'header_comment' => [ - 'commentType' => 'PHPDoc', + 'comment_type' => 'PHPDoc', 'header' => $header, - 'separate' => 'none' + 'separate' => 'bottom' ], 'array_syntax' => [ 'syntax' => 'short' ], + 'encoding' => true, // MUST use only UTF-8 without BOM 'single_quote' => true, 'class_attributes_separation' => true, 'no_unused_imports' => true, + 'global_namespace_import' => true, 'standardize_not_equals' => true, + 'declare_strict_types' => true, ]) ->setFinder( PhpCsFixer\Finder::create() diff --git a/app/Exception/ApiException.php b/app/Exception/ApiException.php index db96cf5f..558b12a4 100644 --- a/app/Exception/ApiException.php +++ b/app/Exception/ApiException.php @@ -1,4 +1,4 @@ - Date: Wed, 15 Apr 2020 12:14:37 +0800 Subject: [PATCH 612/643] up phpcs config --- .php_cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.php_cs b/.php_cs index e644064e..e6077b74 100644 --- a/.php_cs +++ b/.php_cs @@ -15,8 +15,8 @@ return PhpCsFixer\Config::create() '@PSR2' => true, 'header_comment' => [ 'comment_type' => 'PHPDoc', - 'header' => $header, - 'separate' => 'bottom' + 'header' => $header, + 'separate' => 'bottom' ], 'array_syntax' => [ 'syntax' => 'short' From 210243ad798259ae9ce8382c14e79beba0690068 Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 15 Apr 2020 12:16:30 +0800 Subject: [PATCH 613/643] print php-cs-fixer version --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e8b32cba..99b79dc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ install: before_script: - phpenv config-rm xdebug.ini + - bin/php-cs-fixer -V - composer config -g process-timeout 900 && composer update - composer require --dev phpstan/phpstan From f79f37bca6bd62234ce7c5eacb515fe9603675cf Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 15 Apr 2020 13:10:51 +0800 Subject: [PATCH 614/643] add swoft/cache to composer.json --- app/Http/Controller/CacheController.php | 3 --- composer.json | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/Http/Controller/CacheController.php b/app/Http/Controller/CacheController.php index 31e571ff..510aa33d 100644 --- a/app/Http/Controller/CacheController.php +++ b/app/Http/Controller/CacheController.php @@ -30,7 +30,6 @@ class CacheController * * @return array * @throws InvalidArgumentException - * @throws \Psr\SimpleCache\InvalidArgumentException */ public function set(): array { @@ -45,7 +44,6 @@ public function set(): array * * @return array * @throws InvalidArgumentException - * @throws \Psr\SimpleCache\InvalidArgumentException */ public function get(): array { @@ -62,7 +60,6 @@ public function get(): array * * @return array * @throws InvalidArgumentException - * @throws \Psr\SimpleCache\InvalidArgumentException */ public function del(): array { diff --git a/composer.json b/composer.json index a1ef3cb0..541a127d 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "ext-simplexml": "*", "ext-libxml": "*", "ext-mbstring": "*", + "swoft/cache": "~2.0.0", "swoft/db": "~2.0.0", "swoft/i18n": "~2.0.0", "swoft/view": "~2.0.0", From 4f8d7a63206cff26cbc8cf07fd8730b4acb01f86 Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 15 Apr 2020 14:11:47 +0800 Subject: [PATCH 615/643] fix phpstan error --- phpstan.neon.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 57eb2f2a..db758b82 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -27,7 +27,7 @@ parameters: # - '#^Call to an undefined method Swoft\\Server\\Server::disconnect\(\)\.$#' - '#^Call to an undefined method\sPsr\\Http\\Message\\ServerRequestInterface::getUriPath\(\)#' - '#.+onstant APP_DEBUG not found.#' - - '#^Function view not found.#' + # - '#^Function view not found.#' # - '#^Call to static method \w+\(\) on an unknown class Swoft\\Cache\\Cache.#' # These are ignored for now - From b8f1e7a4036e98389533a1b2984df32c9e862d09 Mon Sep 17 00:00:00 2001 From: inhere Date: Wed, 15 Apr 2020 14:21:53 +0800 Subject: [PATCH 616/643] fix phpunit error --- test/bootstrap.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/bootstrap.php b/test/bootstrap.php index 90b41c5e..95509527 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -17,8 +17,6 @@ $loader = require dirname(__DIR__) . '/vendor/autoload.php'; $loader->addPsr4('SwoftTest\\Testing\\', $vendor . '/swoft/framework/test/testing/'); -$application = new TestApplication([ - 'basePath' => $baseDir, -]); +$application = new TestApplication($baseDir); $application->setBeanFile($baseDir . '/app/bean.php'); $application->run(); From 9bbb5ff970aff6cc904d463c703d6887136625b5 Mon Sep 17 00:00:00 2001 From: ChongmingDu Date: Sat, 18 Apr 2020 17:48:05 +0800 Subject: [PATCH 617/643] update: dockerfile --- Dockerfile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 11cc8c6c..3d278343 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,11 +16,12 @@ LABEL maintainer="inhere " version="2.0" # --build-arg timezone=Asia/Shanghai ARG timezone # app env: prod pre test dev -ARG app_env=prod +ARG app_env=test # default use www-data user ARG work_user=www-data -ENV APP_ENV=${app_env:-"prod"} \ +# default APP_ENV = test +ENV APP_ENV=${app_env:-"test"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=5.1.0 \ SWOOLE_VERSION=4.4.17 \ @@ -29,7 +30,7 @@ ENV APP_ENV=${app_env:-"prod"} \ # Libs -y --no-install-recommends RUN apt-get update \ && apt-get install -y \ - curl wget git zip unzip less vim procps lsof tcpdump htop openssl \ + curl wget git zip unzip less vim procps lsof tcpdump htop openssl net-tools iputils-ping \ libz-dev \ libssl-dev \ libnghttp2-dev \ @@ -39,7 +40,9 @@ RUN apt-get update \ libfreetype6-dev \ # Install PHP extensions && docker-php-ext-install \ - bcmath gd pdo_mysql mbstring sockets zip sysvmsg sysvsem sysvshm + bcmath gd pdo_mysql mbstring sockets zip sysvmsg sysvsem sysvshm \ +# Clean apt cache + && rm -rf /var/lib/apt/lists/* # Install composer Run curl -sS https://getcomposer.org/installer | php \ From e05db74aaa3f3c64bf5f7b63e47ca497f3a163d6 Mon Sep 17 00:00:00 2001 From: ChongmingDu Date: Sat, 18 Apr 2020 18:23:53 +0800 Subject: [PATCH 618/643] format spaces --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3d278343..a4c8e6cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,7 @@ ENV APP_ENV=${app_env:-"test"} \ # Libs -y --no-install-recommends RUN apt-get update \ && apt-get install -y \ - curl wget git zip unzip less vim procps lsof tcpdump htop openssl net-tools iputils-ping \ + curl wget git zip unzip less vim procps lsof tcpdump htop openssl net-tools iputils-ping \ libz-dev \ libssl-dev \ libnghttp2-dev \ From 31d04f1fadd139faf1dc3ee88f0986f76bc8aa73 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 19 Apr 2020 22:23:06 +0800 Subject: [PATCH 619/643] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 47260172..3f6b4625 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,9 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Forum https://github.com/swoft-cloud/forum/issues - Gitter.im https://gitter.im/swoft-cloud/community - Reddit https://www.reddit.com/r/swoft/ -- QQ Group1: 548173319 -- QQ Group2: 778656850 +- QQ Group3: 541038173 +- QQ Group2: 778656850(full) +- QQ Group1: 548173319(full) ## Requirement From 846ecdce83ab33d5dc5722581e88e0822ecb61d4 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 19 Apr 2020 22:24:50 +0800 Subject: [PATCH 620/643] Update README.zh-CN.md --- README.zh-CN.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 6d3eab76..4629158a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -27,7 +27,7 @@ Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP ## 功能特色 - - 内置高性能网络服务器(Http/Websocket/RPC/TCP) +- 内置高性能网络服务器(Http/Websocket/RPC/TCP) - 灵活的组件功能 - 强大的注解功能 - 多样化的命令终端(控制台) @@ -62,8 +62,9 @@ Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP ## 学习交流 -- QQ Group1: 548173319 -- QQ Group2: 778656850 +- QQ Group3: 541038173 +- QQ Group2: 778656850(已满) +- QQ Group1: 548173319(已满) - [swoft-cloud/community](https://gitter.im/swoft-cloud/community) ## 免费技术支持 @@ -142,6 +143,7 @@ Component Name | Packagist Version -----------------|--------------------- swoft-apollo | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/apollo.svg)](https://packagist.org/packages/swoft/apollo) swoft-breaker | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/breaker.svg)](https://packagist.org/packages/swoft/breaker) +swoft-cache | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/cache.svg)](https://packagist.org/packages/swoft/cache) swoft-crontab | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/crontab.svg)](https://packagist.org/packages/swoft/crontab) swoft-consul | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/consul.svg)](https://packagist.org/packages/swoft/consul) swoft-limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/limiter.svg)](https://packagist.org/packages/swoft/limiter) From 869732f4234a57ac135b7cee0766d579c6b1ccee Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 26 Apr 2020 14:04:24 +0800 Subject: [PATCH 621/643] update swoole to 4.4.18 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a4c8e6cb..0e12ee3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ ARG work_user=www-data ENV APP_ENV=${app_env:-"test"} \ TIMEZONE=${timezone:-"Asia/Shanghai"} \ PHPREDIS_VERSION=5.1.0 \ - SWOOLE_VERSION=4.4.17 \ + SWOOLE_VERSION=4.4.18 \ COMPOSER_ALLOW_SUPERUSER=1 # Libs -y --no-install-recommends From fa8000cca549a94d6a5358884f1aa56269928cb0 Mon Sep 17 00:00:00 2001 From: Inhere Date: Thu, 30 Apr 2020 21:09:44 +0800 Subject: [PATCH 622/643] Update label.yml --- .github/workflows/label.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index b7052a08..bc48039d 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -20,7 +20,7 @@ jobs: # Github token github-token: "${{ secrets.GITHUB_TOKEN }}" # keywords to look for in the issue - keywords: '["db", "database", "mysql", "pdo"]' + keywords: '["db", "database", "mysql", "pdo", "model"]' # assignees to be assigned when keyword is found assignees: '["sakuraovq"]' # labels to be set when keyword is found @@ -43,7 +43,7 @@ jobs: uses: Naturalclar/issue-action@v1.0.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - keywords: '["env", "config"]' + keywords: '["配置", "config"]' assignees: '["stelin", "inhere"]' labels: '["swoft: config"]' - name: Event Issue Labeler From cce3ee3749c895a1090f111880886ed56158dfcb Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 8 May 2020 11:21:00 +0800 Subject: [PATCH 623/643] add more unit tests examples --- app/Common/MyBean.php | 5 ++ app/Http/Controller/ProcController.php | 40 +++++++++++++ app/Model/Logic/MonitorLogic.php | 15 ++--- app/bean.php | 9 ++- phpunit.xml | 23 ++++---- test/README.md | 53 +++++++++++++++++ test/{apitest => api}/.keep | 0 test/api/ExampleApiTest.php | 39 +++++++++++++ test/api/README.md | 9 +++ test/bootstrap.php | 19 +++++-- test/httptest/.keep | 1 + test/httptest/README.md | 17 ++++++ test/httptest/http-client.env.json | 28 +++++++++ test/httptest/sample.http | 9 +++ test/testdata/.keep | 1 + test/testdata/http-client-tests.png | Bin 0 -> 110030 bytes test/testing/TestApplication.php | 75 +++++++++++++++++++++++++ test/unit/Common/MyBeanTest.php | 30 ++++++++++ test/unit/ExampleTest.php | 5 ++ 19 files changed, 353 insertions(+), 25 deletions(-) create mode 100644 app/Http/Controller/ProcController.php create mode 100644 test/README.md rename test/{apitest => api}/.keep (100%) create mode 100644 test/api/ExampleApiTest.php create mode 100644 test/api/README.md create mode 100644 test/httptest/.keep create mode 100644 test/httptest/README.md create mode 100644 test/httptest/http-client.env.json create mode 100644 test/httptest/sample.http create mode 100644 test/testdata/.keep create mode 100644 test/testdata/http-client-tests.png create mode 100644 test/testing/TestApplication.php create mode 100644 test/unit/Common/MyBeanTest.php diff --git a/app/Common/MyBean.php b/app/Common/MyBean.php index 0a1c0e10..bfb7f0bb 100644 --- a/app/Common/MyBean.php +++ b/app/Common/MyBean.php @@ -27,4 +27,9 @@ public function myMethod(): array return ['hi']; } + + public function myMethod2(): string + { + return __METHOD__; + } } diff --git a/app/Http/Controller/ProcController.php b/app/Http/Controller/ProcController.php new file mode 100644 index 00000000..cdfca67f --- /dev/null +++ b/app/Http/Controller/ProcController.php @@ -0,0 +1,40 @@ +getSwooleProcess()->exportSocket()); + + return 'hello'; + } +} diff --git a/app/Model/Logic/MonitorLogic.php b/app/Model/Logic/MonitorLogic.php index 56be76d8..4ac5bb85 100644 --- a/app/Model/Logic/MonitorLogic.php +++ b/app/Model/Logic/MonitorLogic.php @@ -34,19 +34,20 @@ class MonitorLogic */ public function monitor(Process $process): void { - $process->name('swoft-monitor'); + // \vdump($process->exportSocket()); + // $process->name('swoft-monitor'); while (true) { $connections = context()->getServer()->getSwooleServer()->connections; CLog::info('monitor = ' . json_encode($connections)); // Database - $user = User::find(1)->toArray(); - CLog::info('user=' . json_encode($user)); - - // Redis - Redis::set('test', 'ok'); - CLog::info('test=' . Redis::get('test')); + // $user = User::find(1)->toArray(); + // CLog::info('user=' . json_encode($user)); + // + // // Redis + // Redis::set('test', 'ok'); + // CLog::info('test=' . Redis::get('test')); Coroutine::sleep(3); } diff --git a/app/bean.php b/app/bean.php index 5c498433..a0e8d54b 100644 --- a/app/bean.php +++ b/app/bean.php @@ -41,11 +41,11 @@ // 'tcp' => bean('tcpServer'), ], 'process' => [ - // 'monitor' => bean(MonitorProcess::class) - // 'crontab' => bean(CrontabProcess::class) + // 'monitor' => bean(\App\Process\MonitorProcess::class) + // 'crontab' => bean(CrontabProcess::class) ], 'on' => [ - // SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task + // SwooleEvent::TASK => bean(SyncTaskListener::class), // Enable sync task SwooleEvent::TASK => bean(TaskListener::class), // Enable task must task and finish event SwooleEvent::FINISH => bean(FinishListener::class) ], @@ -130,6 +130,9 @@ ], 'rpcServer' => [ 'class' => ServiceServer::class, + 'listener' => [ + 'http' => bean('httpServer'), + ] ], 'wsServer' => [ 'class' => WebSocketServer::class, diff --git a/phpunit.xml b/phpunit.xml index 2e5c0390..dd0bd0d9 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,14 +8,17 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false"> - - - ./test - - - - - ./app - - + + + ./test/unit + + + ./test/api + + + + + ./app + + diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..fbc05a6b --- /dev/null +++ b/test/README.md @@ -0,0 +1,53 @@ +# Tests + +## Http tests + +**If you want to run http client tests, you must start the http server.** + +### Run http tests + +Can only be run inside phpstorm. + +![http-client-tests](testdata/http-client-tests.png) + +## API tests + +**If you want to run api tests, you must start the http server.** + +## Start server + +```bash +php bin/swoft http:start +# OR +php bin/swoft http:start -d +``` + +### Run api tests + +- use vendor phpunit + +```bash +vendor/bin/phpunit --testsuite apiTests +``` + +- use global installed phpunit + +```bash +phpunit --testsuite apiTests +``` + +## Unit tests + +### Run unit tests + +- use vendor phpunit + +```bash +vendor/bin/phpunit --testsuite unitTests +``` + +- use global installed phpunit + +```bash +phpunit --testsuite unitTests +``` diff --git a/test/apitest/.keep b/test/api/.keep similarity index 100% rename from test/apitest/.keep rename to test/api/.keep diff --git a/test/api/ExampleApiTest.php b/test/api/ExampleApiTest.php new file mode 100644 index 00000000..54852ea2 --- /dev/null +++ b/test/api/ExampleApiTest.php @@ -0,0 +1,39 @@ +http = new HttpClient(); + } + + public function testHi(): void + { + $w = $this->http->get('http://127.0.0.1/hi'); + + $this->assertSame('hi', $w->getBody()->getContents()); + } +} diff --git a/test/api/README.md b/test/api/README.md new file mode 100644 index 00000000..9d64c325 --- /dev/null +++ b/test/api/README.md @@ -0,0 +1,9 @@ +# Http tests + +**If you want to run these tests, you must start the http server.** + +## Start server + +```bash +php bin/swoft http:start +``` diff --git a/test/bootstrap.php b/test/bootstrap.php index 95509527..986a4c5c 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -8,15 +8,24 @@ * @license https://github.com/swoft-cloud/swoft/blob/master/LICENSE */ -use SwoftTest\Testing\TestApplication; +use AppTest\Testing\TestApplication; $baseDir = dirname(__DIR__); $vendor = dirname(__DIR__) . '/vendor'; /** @var \Composer\Autoload\ClassLoader $loader */ $loader = require dirname(__DIR__) . '/vendor/autoload.php'; -$loader->addPsr4('SwoftTest\\Testing\\', $vendor . '/swoft/framework/test/testing/'); -$application = new TestApplication($baseDir); -$application->setBeanFile($baseDir . '/app/bean.php'); -$application->run(); +$swoftFwDir = $vendor . '/swoft/framework'; + +// in framework developing +if (file_exists($vendor . '/swoft/component/src/framework')) { + $swoftFwDir = $vendor . '/swoft/component/src/framework'; +} + +$loader->addPsr4('AppTest\\Unit\\', $baseDir . '/test/unit/'); +$loader->addPsr4('AppTest\\Testing\\', $baseDir . '/test/testing/'); +$loader->addPsr4('SwoftTest\\Testing\\', $swoftFwDir . '/test/testing/'); + +$app = new TestApplication($baseDir); +$app->run(); diff --git a/test/httptest/.keep b/test/httptest/.keep new file mode 100644 index 00000000..18e1ba37 --- /dev/null +++ b/test/httptest/.keep @@ -0,0 +1 @@ +If you want run api test, must start an server before run tests. diff --git a/test/httptest/README.md b/test/httptest/README.md new file mode 100644 index 00000000..6e10a8e8 --- /dev/null +++ b/test/httptest/README.md @@ -0,0 +1,17 @@ +# Http tests + +**If you want to run these tests, you must start the http server.** + +## Config + +Please see http-client.env.json + +## Start server + +```bash +php bin/swoft http:start +``` + +## Run + +Can only be run inside phpstorm diff --git a/test/httptest/http-client.env.json b/test/httptest/http-client.env.json new file mode 100644 index 00000000..3251c960 --- /dev/null +++ b/test/httptest/http-client.env.json @@ -0,0 +1,28 @@ +{ + "development": { + "host": "127.0.0.1:10106", + "bid": 10006, + "id-value": 12345, + "username": "", + "password": "", + "my-var": "my-dev-value", + "form-type": "application/x-www-form-urlencoded" + }, + "testing": { + "host": "", + "bid": 10001, + "id-value": 12345, + "username": "", + "password": "", + "my-var": "my-dev-value", + "form-type": "application/x-www-form-urlencoded" + }, + "production": { + "host": "example.com", + "bid": 10001, + "id-value": 6789, + "username": "", + "password": "", + "my-var": "my-prod-value" + } +} diff --git a/test/httptest/sample.http b/test/httptest/sample.http new file mode 100644 index 00000000..244eaab9 --- /dev/null +++ b/test/httptest/sample.http @@ -0,0 +1,9 @@ +### test api +GET http://{{host}}/hi +Accept: text/plain + +### test api2 +GET http://{{host}}/hello +Accept: text/plain + +### diff --git a/test/testdata/.keep b/test/testdata/.keep new file mode 100644 index 00000000..438a9d29 --- /dev/null +++ b/test/testdata/.keep @@ -0,0 +1 @@ +storage same temp files for tests diff --git a/test/testdata/http-client-tests.png b/test/testdata/http-client-tests.png new file mode 100644 index 0000000000000000000000000000000000000000..160624dd14aefdeea4c2a7c50c10061701a23d6c GIT binary patch literal 110030 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�-kMHtj5x^?PuVObJ)SCw6w?jfdqPkms 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

          }M+|_I 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 B0;vE1 literal 0 HcmV?d00001 diff --git a/test/testing/TestApplication.php b/test/testing/TestApplication.php new file mode 100644 index 00000000..0ebb802b --- /dev/null +++ b/test/testing/TestApplication.php @@ -0,0 +1,75 @@ +setStartConsole(false); + + $config = array_merge([ + 'basePath' => $basePath, + 'beanFile' => $basePath . '/app/bean.php', + 'disabledAutoLoaders' => [ + // \App\AutoLoader::class => 1, + ], + ], $config); + + parent::__construct($config); + } + + public function getCLoggerConfig(): array + { + $config = parent::getCLoggerConfig(); + + // Dont print log to terminal + // $config['enable'] = false; + $config['levels'] = 'error'; + + return $config; + } +} diff --git a/test/unit/Common/MyBeanTest.php b/test/unit/Common/MyBeanTest.php new file mode 100644 index 00000000..11f39078 --- /dev/null +++ b/test/unit/Common/MyBeanTest.php @@ -0,0 +1,30 @@ +assertSame(MyBean::class . '::myMethod2', $bean->myMethod2()); + } +} diff --git a/test/unit/ExampleTest.php b/test/unit/ExampleTest.php index 7de7125b..7547dc00 100644 --- a/test/unit/ExampleTest.php +++ b/test/unit/ExampleTest.php @@ -13,6 +13,11 @@ use PHPUnit\Framework\TestCase; use function bean; +/** + * Class ExampleTest + * + * @package AppTest\Unit + */ class ExampleTest extends TestCase { public function testDemo(): void From abbfe7fce24eefb71e4bdb643ff3f5e3b911fe98 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 8 May 2020 11:31:18 +0800 Subject: [PATCH 624/643] update some for composer.json --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 541a127d..f82917bc 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ "swoft/devtool": "~2.0.0" }, "require-dev": { + "swoft/swlib": "~2.0.0", "swoft/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^7.5" }, From f0f8b8decdd1f039e479f504ef0ba7bdb72671ac Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 8 May 2020 19:52:58 +0800 Subject: [PATCH 625/643] fix phpstan error --- app/Http/Controller/CacheController.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/Http/Controller/CacheController.php b/app/Http/Controller/CacheController.php index 510aa33d..26ed9507 100644 --- a/app/Http/Controller/CacheController.php +++ b/app/Http/Controller/CacheController.php @@ -12,7 +12,6 @@ use InvalidArgumentException; use Swoft\Cache\Cache; -use Swoft\Http\Message\Response; use Swoft\Http\Server\Annotation\Mapping\Controller; use Swoft\Http\Server\Annotation\Mapping\RequestMapping; @@ -63,9 +62,6 @@ public function get(): array */ public function del(): array { - /** @var Response $resp */ - // $resp = context()->getResponse(); - return ['del' => Cache::delete('ckey')]; } } From 26142af13bbdfc82ec1ac23b3e445d33a2143f41 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 8 May 2020 20:11:45 +0800 Subject: [PATCH 626/643] update osme for apitest demo --- .travis.yml | 2 +- test/README.md | 20 ++------------------ test/api/.keep | 1 - 3 files changed, 3 insertions(+), 20 deletions(-) delete mode 100644 test/api/.keep diff --git a/.travis.yml b/.travis.yml index 99b79dc5..5a453795 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ install: - | echo "no" | pecl install -f redis - | - wget https://github.com/swoole/swoole-src/archive/v4.4.17.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole + wget https://github.com/swoole/swoole-src/archive/v4.4.18.tar.gz -O swoole.tar.gz && mkdir -p swoole && tar -xf swoole.tar.gz -C swoole --strip-components=1 && rm swoole.tar.gz && cd swoole && phpize && ./configure && make -j$(nproc) && make install && cd - && rm -rf swoole echo "extension = swoole.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - | wget -O bin/php-cs-fixer "https://cs.symfony.com/download/php-cs-fixer-v2.phar" diff --git a/test/README.md b/test/README.md index fbc05a6b..ff152284 100644 --- a/test/README.md +++ b/test/README.md @@ -24,30 +24,14 @@ php bin/swoft http:start -d ### Run api tests -- use vendor phpunit - -```bash -vendor/bin/phpunit --testsuite apiTests -``` - -- use global installed phpunit - ```bash -phpunit --testsuite apiTests +php run.php -c phpunit.xml --testsuite apiTests ``` ## Unit tests ### Run unit tests -- use vendor phpunit - -```bash -vendor/bin/phpunit --testsuite unitTests -``` - -- use global installed phpunit - ```bash -phpunit --testsuite unitTests +php run.php -c phpunit.xml --testsuite unitTests ``` diff --git a/test/api/.keep b/test/api/.keep deleted file mode 100644 index 18e1ba37..00000000 --- a/test/api/.keep +++ /dev/null @@ -1 +0,0 @@ -If you want run api test, must start an server before run tests. From be895f7867100904b4938401eed5e3fd956aa4f9 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 8 May 2020 20:26:16 +0800 Subject: [PATCH 627/643] fix api tests error --- test/README.md | 4 ++-- test/api/ExampleApiTest.php | 7 ++++++- test/bootstrap.php | 1 + test/httptest/.keep | 1 - test/httptest/http-client.env.json | 11 +++-------- test/httptest/sample.http | 8 +++++++- 6 files changed, 19 insertions(+), 13 deletions(-) delete mode 100644 test/httptest/.keep diff --git a/test/README.md b/test/README.md index ff152284..648da8a2 100644 --- a/test/README.md +++ b/test/README.md @@ -25,7 +25,7 @@ php bin/swoft http:start -d ### Run api tests ```bash -php run.php -c phpunit.xml --testsuite apiTests +php test/run.php -c phpunit.xml --testsuite apiTests ``` ## Unit tests @@ -33,5 +33,5 @@ php run.php -c phpunit.xml --testsuite apiTests ### Run unit tests ```bash -php run.php -c phpunit.xml --testsuite unitTests +php test/run.php -c phpunit.xml --testsuite unitTests ``` diff --git a/test/api/ExampleApiTest.php b/test/api/ExampleApiTest.php index 54852ea2..f5dd8d50 100644 --- a/test/api/ExampleApiTest.php +++ b/test/api/ExampleApiTest.php @@ -10,6 +10,7 @@ namespace AppTest\Api; +use App\Http\Controller\HomeController; use PHPUnit\Framework\TestCase; use Swoft\Swlib\HttpClient; @@ -20,6 +21,8 @@ */ class ExampleApiTest extends TestCase { + public const HOST = 'http://127.0.0.1:18306/'; + /** * @var HttpClient */ @@ -32,8 +35,10 @@ public function setUp(): void public function testHi(): void { - $w = $this->http->get('http://127.0.0.1/hi'); + /** @see HomeController::hi() */ + $w = $this->http->get(self::HOST. '/hi'); + $this->assertSame(200, $w->getStatusCode()); $this->assertSame('hi', $w->getBody()->getContents()); } } diff --git a/test/bootstrap.php b/test/bootstrap.php index 986a4c5c..8df079b2 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -26,6 +26,7 @@ $loader->addPsr4('AppTest\\Unit\\', $baseDir . '/test/unit/'); $loader->addPsr4('AppTest\\Testing\\', $baseDir . '/test/testing/'); $loader->addPsr4('SwoftTest\\Testing\\', $swoftFwDir . '/test/testing/'); +$loader->addPsr4('Swoft\\Swlib\\', $vendor . '/swoft/swlib/src/'); $app = new TestApplication($baseDir); $app->run(); diff --git a/test/httptest/.keep b/test/httptest/.keep deleted file mode 100644 index 18e1ba37..00000000 --- a/test/httptest/.keep +++ /dev/null @@ -1 +0,0 @@ -If you want run api test, must start an server before run tests. diff --git a/test/httptest/http-client.env.json b/test/httptest/http-client.env.json index 3251c960..6eb0a55f 100644 --- a/test/httptest/http-client.env.json +++ b/test/httptest/http-client.env.json @@ -1,25 +1,20 @@ { "development": { - "host": "127.0.0.1:10106", - "bid": 10006, + "host": "127.0.0.1:18306", "id-value": 12345, "username": "", "password": "", - "my-var": "my-dev-value", - "form-type": "application/x-www-form-urlencoded" + "my-var": "my-dev-value" }, "testing": { "host": "", - "bid": 10001, "id-value": 12345, "username": "", "password": "", - "my-var": "my-dev-value", - "form-type": "application/x-www-form-urlencoded" + "my-var": "my-dev-value" }, "production": { "host": "example.com", - "bid": 10001, "id-value": 6789, "username": "", "password": "", diff --git a/test/httptest/sample.http b/test/httptest/sample.http index 244eaab9..1f7c380f 100644 --- a/test/httptest/sample.http +++ b/test/httptest/sample.http @@ -1,6 +1,12 @@ ### test api GET http://{{host}}/hi -Accept: text/plain +Accept: text/html + +> {% +client.test("Request executed successfully", function() { + client.assert(response.status === 200, "Response status is not 200"); +}); +%} ### test api2 GET http://{{host}}/hello From 00cbd635a4fba2ffd7b89dc688606044f22a1ae6 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 8 May 2020 20:40:52 +0800 Subject: [PATCH 628/643] update some for travis config --- .travis.yml | 4 +++- composer.json | 7 +++---- test/bootstrap.php | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5a453795..6cbbe5b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,5 +27,7 @@ before_script: script: - composer check-cs - - composer test + - composer phpstan + - composer api-tests + - composer unit-tests # - php bin/swoft dinfo:env diff --git a/composer.json b/composer.json index f82917bc..91988b4a 100644 --- a/composer.json +++ b/composer.json @@ -61,10 +61,9 @@ "post-root-package-install": [ "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" ], - "test": [ - "./vendor/bin/phpstan analyze", - "./vendor/bin/phpunit -c phpunit.xml" - ], + "phpstan": "./vendor/bin/phpstan analyze", + "api-tests": "php test/run.php -c phpunit.xml --testsuite apiTests", + "unit-tests": "php test/run.php -c phpunit.xml --testsuite unitTests", "check-cs": "./bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff", "cs-fix": "./bin/php-cs-fixer fix" } diff --git a/test/bootstrap.php b/test/bootstrap.php index 8df079b2..74d3e49d 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -26,7 +26,7 @@ $loader->addPsr4('AppTest\\Unit\\', $baseDir . '/test/unit/'); $loader->addPsr4('AppTest\\Testing\\', $baseDir . '/test/testing/'); $loader->addPsr4('SwoftTest\\Testing\\', $swoftFwDir . '/test/testing/'); -$loader->addPsr4('Swoft\\Swlib\\', $vendor . '/swoft/swlib/src/'); +// $loader->addPsr4('Swoft\\Swlib\\', $vendor . '/swoft/swlib/src/'); $app = new TestApplication($baseDir); $app->run(); From c86d6f62b4b75eb94df758e4773af6a1bb51f586 Mon Sep 17 00:00:00 2001 From: inhere Date: Sat, 9 May 2020 10:06:13 +0800 Subject: [PATCH 629/643] update for api tests --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6cbbe5b4..45dde42b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,8 @@ before_script: - bin/php-cs-fixer -V - composer config -g process-timeout 900 && composer update - composer require --dev phpstan/phpstan + # start http server for api-tests + - php bin/swoft http:start -d script: - composer check-cs @@ -31,3 +33,6 @@ script: - composer api-tests - composer unit-tests # - php bin/swoft dinfo:env + +after_script: + - php bin/swoft http:stop From f51c9933c559cb7e628c474c90ce40e9a0277fdb Mon Sep 17 00:00:00 2001 From: Inhere Date: Mon, 18 May 2020 19:53:27 +0800 Subject: [PATCH 630/643] Create config.yml --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..393a2216 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Swoft Chinese Documents + url: https://swoft.org + about: Please ask and answer questions here. + - name: Swoft English Documents + url: http://swoft.io + about: Please report security vulnerabilities here. From 42b94659508198584e100a6bd40d4e5332b2772d Mon Sep 17 00:00:00 2001 From: Gulzar Ahmed Date: Wed, 29 Jul 2020 18:46:59 +0530 Subject: [PATCH 631/643] Typo Fix: CORNTAB to CRONTAB --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f6b4625..57b2f5d4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Flexible annotation function - Diversified command terminal(Console) - Powerful Aspect Oriented Programming(AOP) -- Perfect Container management、Dependency Injection (DI) +- Perfect Container management, Dependency Injection (DI) - Flexible event mechanism - Implementation of HTTP message based on PSR-7 - Event Manager Based on PSR-14 @@ -39,7 +39,7 @@ Through three years of accumulation and direction exploration, Swoft has made Sw - Database is highly compatible Laravel - Cache Redis highly compatible Laravel - Efficient task processing -- Efficient seconds corntab +- Efficient seconds crontab - Process pool - Flexible exception handling - Powerful log system From b7a990115fea30773c0ef284b1a00b1ce448fee5 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2020 09:13:52 +0000 Subject: [PATCH 632/643] Create Dependabot config file --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..c630ffa6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: +- package-ecosystem: composer + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 From b98e5017326bfa6f0a0f46d51739e5c64eeb94dc Mon Sep 17 00:00:00 2001 From: "jj.style" Date: Tue, 11 Aug 2020 17:39:46 +0800 Subject: [PATCH 633/643] correct the docs links --- README.zh-CN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 4629158a..4566f1b8 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -57,8 +57,8 @@ Swoft 通过长达三年的积累和方向的探索,把 Swoft 打造成 PHP ## 在线文档 -- [中文文档](https://www.swoft.org/docs/2.x/zh-CN/README.html) -- [English](https://www.swoft.org/docs/2.x/en) +- [中文文档](https://www.swoft.org/documents/v2/index.html) +- [English](http://swoft.io/docs/2.x/en) ## 学习交流 From 543d4cd8e6bbdc2294f63b69ac796899bd609033 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 11 Aug 2020 19:47:40 +0800 Subject: [PATCH 634/643] Update README.zh-CN.md --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 4629158a..aebaa6d8 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -9,7 +9,7 @@ [![Docker Build Status](https://img.shields.io/docker/build/swoft/swoft.svg)](https://hub.docker.com/r/swoft/swoft/) [![Php Version](https://img.shields.io/badge/php-%3E=7.1-brightgreen.svg?maxAge=2592000)](https://secure.php.net/) [![Swoole Version](https://img.shields.io/badge/swoole-%3E=4.3.3-brightgreen.svg?maxAge=2592000)](https://github.com/swoole/swoole-src) -[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org/docs) +[![Swoft Doc](https://img.shields.io/badge/docs-passing-green.svg?maxAge=2592000)](https://www.swoft.org) [![Swoft License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/swoft-cloud/swoft/blob/master/LICENSE) [![Gitter](https://img.shields.io/gitter/room/swoft-cloud/swoft.svg)](https://gitter.im/swoft-cloud/community) From 7109da511c80342afcf682da45d914ad89d7e9f3 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sun, 13 Sep 2020 14:39:57 +0800 Subject: [PATCH 635/643] supprot muti mig db pool --- app/Migration/User.php | 2 +- bin/swoft | 0 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 bin/swoft diff --git a/app/Migration/User.php b/app/Migration/User.php index 2292766d..9f04f459 100644 --- a/app/Migration/User.php +++ b/app/Migration/User.php @@ -18,7 +18,7 @@ * * @since 2.0 * - * @Migration(20190630164222) + * @Migration(time=20190630164222, pool="db.pool,db1.pool") */ class User extends BaseMigration { diff --git a/bin/swoft b/bin/swoft old mode 100644 new mode 100755 From c39436108a07b8b4c7f8a399d0e01f5248832dd6 Mon Sep 17 00:00:00 2001 From: zhenghongyang Date: Sun, 13 Sep 2020 15:02:37 +0800 Subject: [PATCH 636/643] perfect db demo --- app/bean.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/bean.php b/app/bean.php index a0e8d54b..ce36b774 100644 --- a/app/bean.php +++ b/app/bean.php @@ -84,6 +84,7 @@ 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', 'password' => 'swoft123456', + 'charset' => 'utf8mb4', // 'dbSelector' => bean(DbSelector::class) ], 'db2.pool' => [ @@ -94,7 +95,8 @@ 'class' => Database::class, 'dsn' => 'mysql:dbname=test2;host=127.0.0.1', 'username' => 'root', - 'password' => 'swoft123456' + 'password' => 'swoft123456', + 'charset' => 'utf8mb4', ], 'db3.pool' => [ 'class' => Pool::class, From 114de481db59b876de96a559969baa77d7ddb753 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 13 Sep 2020 22:57:17 +0800 Subject: [PATCH 637/643] update some examples --- .php_cs | 21 +++--- app/Http/Middleware/DomainLimitMiddleware.php | 67 +++++++++++++++++++ app/Http/Middleware/FavIconMiddleware.php | 5 +- test/README.md | 26 ++++--- 4 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 app/Http/Middleware/DomainLimitMiddleware.php diff --git a/.php_cs b/.php_cs index e6077b74..83e4640a 100644 --- a/.php_cs +++ b/.php_cs @@ -13,21 +13,24 @@ return PhpCsFixer\Config::create() ->setRiskyAllowed(true) ->setRules([ '@PSR2' => true, - 'header_comment' => [ - 'comment_type' => 'PHPDoc', - 'header' => $header, - 'separate' => 'bottom' - ], 'array_syntax' => [ 'syntax' => 'short' ], - 'encoding' => true, // MUST use only UTF-8 without BOM - 'single_quote' => true, 'class_attributes_separation' => true, + 'declare_strict_types' => true, + 'encoding' => true, // MUST use only UTF-8 without BOM + 'global_namespace_import' => [ + 'import_constants' => true, + 'import_functions' => true, + ], + 'header_comment' => [ + 'comment_type' => 'PHPDoc', + 'header' => $header, + 'separate' => 'bottom' + ], 'no_unused_imports' => true, - 'global_namespace_import' => true, + 'single_quote' => true, 'standardize_not_equals' => true, - 'declare_strict_types' => true, ]) ->setFinder( PhpCsFixer\Finder::create() diff --git a/app/Http/Middleware/DomainLimitMiddleware.php b/app/Http/Middleware/DomainLimitMiddleware.php new file mode 100644 index 00000000..bfd49321 --- /dev/null +++ b/app/Http/Middleware/DomainLimitMiddleware.php @@ -0,0 +1,67 @@ + [ + // match all /user/* + '/user/', + ], + 'blog.com' => [ + // match all /blog/* + '/blog/', + ] + ]; + + /** + * Process an incoming server request. + * + * @param ServerRequestInterface|Request $request + * @param RequestHandlerInterface $handler + * + * @return ResponseInterface + * @inheritdoc + */ + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $uriPath = $request->getUriPath(); + $domain = $request->getUri()->getHost(); + + if (!isset($this->domain2paths[$domain])) { + return context()->getResponse()->withStatus(404)->withContent('invalid request domain'); + } + + foreach ($this->domain2paths[$domain] as $prefix) { + // not match route prefix + if (strpos($uriPath, $prefix) !== 0) { + return context()->getResponse()->withStatus(404)->withContent('page not found'); + } + } + + return $handler->handle($request); + } +} diff --git a/app/Http/Middleware/FavIconMiddleware.php b/app/Http/Middleware/FavIconMiddleware.php index 12ff387a..f5a4de80 100644 --- a/app/Http/Middleware/FavIconMiddleware.php +++ b/app/Http/Middleware/FavIconMiddleware.php @@ -14,7 +14,6 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Swoft\Bean\Annotation\Mapping\Bean; -use Swoft\Exception\SwoftException; use Swoft\Http\Message\Request; use Swoft\Http\Server\Contract\MiddlewareInterface; use function context; @@ -34,10 +33,12 @@ class FavIconMiddleware implements MiddlewareInterface * * @return ResponseInterface * @inheritdoc - * @throws SwoftException */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { + $uriPath = $request->getUriPath(); + $domain = $request->getUri()->getHost(); + if ($request->getUriPath() === '/favicon.ico') { return context()->getResponse()->withStatus(404); } diff --git a/test/README.md b/test/README.md index 648da8a2..e4b70cca 100644 --- a/test/README.md +++ b/test/README.md @@ -1,19 +1,11 @@ # Tests -## Http tests - -**If you want to run http client tests, you must start the http server.** - -### Run http tests - -Can only be run inside phpstorm. - -![http-client-tests](testdata/http-client-tests.png) - ## API tests **If you want to run api tests, you must start the http server.** +tests case dir: `/test/api/` + ## Start server ```bash @@ -30,8 +22,22 @@ php test/run.php -c phpunit.xml --testsuite apiTests ## Unit tests +tests case dir: `/test/unit/` + ### Run unit tests ```bash php test/run.php -c phpunit.xml --testsuite unitTests ``` + +## Http tests + +tests case dir: `/test/httptest/` + +**If you want to run http client tests, you must start the http server.** + +### Run http tests + +Can only be run inside phpstorm. + +![http-client-tests](testdata/http-client-tests.png) From 85df5e25cb98d306756e01c28d31acc063f570c1 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 13 Sep 2020 23:08:55 +0800 Subject: [PATCH 638/643] upsome --- test/unit/Common/MyBeanTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unit/Common/MyBeanTest.php b/test/unit/Common/MyBeanTest.php index 11f39078..bde9377e 100644 --- a/test/unit/Common/MyBeanTest.php +++ b/test/unit/Common/MyBeanTest.php @@ -12,6 +12,7 @@ use App\Common\MyBean; use PHPUnit\Framework\TestCase; +use Swoft\Log\Helper\Log; use function bean; /** @@ -25,6 +26,9 @@ public function testMyMethod2(): void { $bean = bean(MyBean::class); + vdump('test message'); + Log::info('test message'); + $this->assertSame(MyBean::class . '::myMethod2', $bean->myMethod2()); } } From 2599c30ec22d2a0bbeb89851681dff28691f3b30 Mon Sep 17 00:00:00 2001 From: Inhere Date: Wed, 9 Dec 2020 10:38:51 +0800 Subject: [PATCH 639/643] Create release.yml --- .github/workflows/release.yml | 62 +++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..a33279f9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,62 @@ +name: Tag-release + +on: + push: + tags: + - v* + +jobs: + release: + name: Test on php ${{ matrix.php}} + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: true + matrix: + php: [7.3] + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set ENV for github-release + # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable + run: | + echo "RELEASE_TAG=${GITHUB_REF:10}" >> $GITHUB_ENV + echo "RELEASE_NAME=$GITHUB_WORKFLOW" >> $GITHUB_ENV + + # usage refer https://github.com/shivammathur/setup-php + - name: Setup PHP + timeout-minutes: 5 + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php}} + tools: pecl, php-cs-fixer, phpunit + extensions: mbstring, dom, fileinfo, mysql, openssl # , swoole-4.4.19 #optional, setup extensions + ini-values: post_max_size=56M, short_open_tag=On #optional, setup php.ini configuration + coverage: none #optional, setup coverage driver: xdebug, none + + - name: Install dependencies # eg: v1.0.3 + run: | + tag1=${GITHUB_REF#refs/*/} + echo "release tag: ${tag1}" + composer install --no-progress --no-suggest + + # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit" + # Docs: https://getcomposer.org/doc/articles/scripts.md + +# - name: Build phar and send to github assets +# run: | +# echo $RELEASE_TAG +# echo $RELEASE_NAME +# php -d phar.readonly=0 bin/kite phar:pack -o kite-${RELEASE_TAG}.phar --no-progress +# php kite-${RELEASE_TAG}.phar -V + + # https://github.com/actions/create-release + - uses: meeDamian/github-release@2.0 + with: + gzip: false + token: ${{ secrets.GITHUB_TOKEN }} + tag: ${{ env.RELEASE_TAG }} + name: ${{ env.RELEASE_TAG }} +# files: kite-${{ env.RELEASE_TAG }}.phar From 02430c87cc4ec42590af93d9cc7c7b205743add7 Mon Sep 17 00:00:00 2001 From: inhere Date: Sun, 2 May 2021 00:03:07 +0800 Subject: [PATCH 640/643] update for phpunit version --- composer.cn.json | 2 +- composer.json | 2 +- dev.composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.cn.json b/composer.cn.json index a63797b5..376ba4d0 100644 --- a/composer.cn.json +++ b/composer.cn.json @@ -27,7 +27,7 @@ }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5", + "phpunit/phpunit": "^7.5 || ^8.0", "swoft/devtool": "~2.0.0" }, "autoload": { diff --git a/composer.json b/composer.json index 91988b4a..46570ed4 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "require-dev": { "swoft/swlib": "~2.0.0", "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^7.5 || ^8.0" }, "autoload": { "psr-4": { diff --git a/dev.composer.json b/dev.composer.json index 99052245..f2c200e5 100644 --- a/dev.composer.json +++ b/dev.composer.json @@ -18,7 +18,7 @@ }, "require-dev": { "swoft/swoole-ide-helper": "dev-master", - "phpunit/phpunit": "^7.5" + "phpunit/phpunit": "^7.5 || ^8.0" }, "autoload": { "psr-4": { From 9bf50ff42e21c9fdb96021a29192a60eaff56ba1 Mon Sep 17 00:00:00 2001 From: inhere Date: Fri, 7 May 2021 00:11:55 +0800 Subject: [PATCH 641/643] update some for composer dev json --- dev.composer.json => composer.dev.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename dev.composer.json => composer.dev.json (97%) diff --git a/dev.composer.json b/composer.dev.json similarity index 97% rename from dev.composer.json rename to composer.dev.json index f2c200e5..3544559b 100644 --- a/dev.composer.json +++ b/composer.dev.json @@ -12,11 +12,11 @@ "php": ">7.1", "ext-pdo": "*", "ext-json": "*", - "ext-swoole": ">=4.3", "swoft/component": "dev-master as 2.0", "swoft/ext": "dev-master as 2.0" }, "require-dev": { + "swoft/swlib": "~2.0.0", "swoft/swoole-ide-helper": "dev-master", "phpunit/phpunit": "^7.5 || ^8.0" }, From 215b203b601fe805571d643b39b8a0733fa90048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=9C=96?= Date: Mon, 17 Jan 2022 10:26:14 +0800 Subject: [PATCH 642/643] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 57b2f5d4..3e6df3af 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ swoft-limiter | [![Latest Stable Version](http://img.shields.io/packagist/v/swof swoft-view | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/view.svg)](https://packagist.org/packages/swoft/view) swoft-whoops | [![Latest Stable Version](http://img.shields.io/packagist/v/swoft/whoops.svg)](https://packagist.org/packages/swoft/whoops) + ## License Swoft is an open-source software licensed under the [LICENSE](LICENSE) From a658ae00fcab2dabad31dac56b95d3178793ba0d Mon Sep 17 00:00:00 2001 From: Vladimir Date: Sun, 31 Jul 2022 14:42:52 +0300 Subject: [PATCH 643/643] Add .gitattributes, exclude tests from archive --- .gitattributes | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..54adf57f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +/.editorconfig export-ignore +/.gitattributes export-ignore +/.github export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/phpunit.xml export-ignore +/test export-ignore